feature #16317 Rely on iconv and symfony/polyfill-* (nicolas-grekas)

This PR was merged into the 2.8 branch.

Discussion
----------

Rely on iconv and symfony/polyfill-*

| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | no
| BC breaks?    | no
| Deprecations? | yes
| Tests pass?   | yes
| Fixed tickets | #16240
| License       | MIT
| Doc PR        | -

Status: needs work
symfony/polyfill-* packages are not ready yet. Still, review welcomed.

Commits
-------

303f05b Rely on iconv and symfony/polyfill-*
This commit is contained in:
Fabien Potencier 2015-10-27 19:34:04 -07:00
commit 6557e85faf
91 changed files with 158 additions and 766 deletions

View File

@ -21,7 +21,13 @@
"twig/twig": "~1.20|~2.0", "twig/twig": "~1.20|~2.0",
"psr/log": "~1.0", "psr/log": "~1.0",
"symfony/security-acl": "~2.7", "symfony/security-acl": "~2.7",
"paragonie/random_compat": "~1.0" "symfony/polyfill-intl-icu": "~1.0",
"symfony/polyfill-mbstring": "~1.0",
"symfony/polyfill-php54": "~1.0",
"symfony/polyfill-php55": "~1.0",
"symfony/polyfill-php56": "~1.0",
"symfony/polyfill-php70": "~1.0",
"symfony/polyfill-util": "~1.0"
}, },
"replace": { "replace": {
"symfony/asset": "self.version", "symfony/asset": "self.version",
@ -77,7 +83,6 @@
"doctrine/orm": "~2.4,>=2.4.5", "doctrine/orm": "~2.4,>=2.4.5",
"doctrine/doctrine-bundle": "~1.2", "doctrine/doctrine-bundle": "~1.2",
"monolog/monolog": "~1.11", "monolog/monolog": "~1.11",
"ircmaxell/password-compat": "~1.0",
"ocramius/proxy-manager": "~0.4|~1.0", "ocramius/proxy-manager": "~0.4|~1.0",
"egulias/email-validator": "~1.2", "egulias/email-validator": "~1.2",
"phpdocumentor/reflection": "^1.0.7" "phpdocumentor/reflection": "^1.0.7"
@ -96,10 +101,8 @@
"Symfony\\Component\\": "src/Symfony/Component/" "Symfony\\Component\\": "src/Symfony/Component/"
}, },
"classmap": [ "classmap": [
"src/Symfony/Component/HttpFoundation/Resources/stubs",
"src/Symfony/Component/Intl/Resources/stubs" "src/Symfony/Component/Intl/Resources/stubs"
], ]
"files": [ "src/Symfony/Component/Intl/Resources/stubs/functions.php" ]
}, },
"minimum-stability": "dev", "minimum-stability": "dev",
"extra": { "extra": {

View File

@ -99,16 +99,9 @@ class DbalLogger implements SQLLogger
} }
// detect if the too long string must be shorten // detect if the too long string must be shorten
if (function_exists('mb_strlen')) { if (self::MAX_STRING_LENGTH < iconv_strlen($params[$index], 'UTF-8')) {
if (self::MAX_STRING_LENGTH < mb_strlen($params[$index], 'UTF-8')) { $params[$index] = iconv_substr($params[$index], 0, self::MAX_STRING_LENGTH - 6, 'UTF-8').' [...]';
$params[$index] = mb_substr($params[$index], 0, self::MAX_STRING_LENGTH - 6, 'UTF-8').' [...]'; continue;
continue;
}
} else {
if (self::MAX_STRING_LENGTH < strlen($params[$index])) {
$params[$index] = substr($params[$index], 0, self::MAX_STRING_LENGTH - 6).' [...]';
continue;
}
} }
} }

View File

@ -132,9 +132,6 @@ class DbalLoggerTest extends \PHPUnit_Framework_TestCase
)); ));
} }
/**
* @requires extension mbstring
*/
public function testLogUTF8LongString() public function testLogUTF8LongString()
{ {
$logger = $this->getMock('Psr\\Log\\LoggerInterface'); $logger = $this->getMock('Psr\\Log\\LoggerInterface');
@ -160,7 +157,7 @@ class DbalLoggerTest extends \PHPUnit_Framework_TestCase
$dbalLogger $dbalLogger
->expects($this->once()) ->expects($this->once())
->method('log') ->method('log')
->with('SQL', array('short' => $shortString, 'long' => mb_substr($longString, 0, DbalLogger::MAX_STRING_LENGTH - 6, mb_detect_encoding($longString)).' [...]')) ->with('SQL', array('short' => $shortString, 'long' => iconv_substr($longString, 0, DbalLogger::MAX_STRING_LENGTH - 6, 'UTF-8').' [...]'))
; ;
$dbalLogger->startQuery('SQL', array( $dbalLogger->startQuery('SQL', array(

View File

@ -24,7 +24,6 @@ class SymfonyTestsListener extends \PHPUnit_Framework_BaseTestListener
private $skippedFile = false; private $skippedFile = false;
private $wasSkipped = array(); private $wasSkipped = array();
private $isSkipped = array(); private $isSkipped = array();
private $testsStack = array();
public function __destruct() public function __destruct()
{ {

View File

@ -24,7 +24,7 @@
"symfony/finder": "~2.3|~3.0.0", "symfony/finder": "~2.3|~3.0.0",
"symfony/form": "~2.8", "symfony/form": "~2.8",
"symfony/http-kernel": "~2.8|~3.0.0", "symfony/http-kernel": "~2.8|~3.0.0",
"symfony/intl": "~2.3|~3.0.0", "symfony/polyfill-intl-icu": "~1.0",
"symfony/routing": "~2.2|~3.0.0", "symfony/routing": "~2.2|~3.0.0",
"symfony/templating": "~2.1|~3.0.0", "symfony/templating": "~2.1|~3.0.0",
"symfony/translation": "~2.7|~3.0.0", "symfony/translation": "~2.7|~3.0.0",

View File

@ -240,7 +240,7 @@ EOF
{ {
$string = trim(preg_replace('/\s+/', ' ', $string)); $string = trim(preg_replace('/\s+/', ' ', $string));
if (function_exists('mb_strlen') && false !== $encoding = mb_detect_encoding($string)) { if (false !== $encoding = mb_detect_encoding($string, null, true)) {
if (mb_strlen($string, $encoding) > $length) { if (mb_strlen($string, $encoding) > $length) {
return mb_substr($string, 0, $length - 3, $encoding).'...'; return mb_substr($string, 0, $length - 3, $encoding).'...';
} }

View File

@ -24,6 +24,7 @@
"symfony/event-dispatcher": "~2.8|~3.0.0", "symfony/event-dispatcher": "~2.8|~3.0.0",
"symfony/http-foundation": "~2.4.9|~2.5,>=2.5.4|~3.0.0", "symfony/http-foundation": "~2.4.9|~2.5,>=2.5.4|~3.0.0",
"symfony/http-kernel": "~2.8", "symfony/http-kernel": "~2.8",
"symfony/polyfill-mbstring": "~1.0",
"symfony/filesystem": "~2.3|~3.0.0", "symfony/filesystem": "~2.3|~3.0.0",
"symfony/routing": "~2.8|~3.0.0", "symfony/routing": "~2.8|~3.0.0",
"symfony/security-core": "~2.6|~3.0.0", "symfony/security-core": "~2.6|~3.0.0",
@ -40,7 +41,7 @@
"symfony/css-selector": "~2.0,>=2.0.5|~3.0.0", "symfony/css-selector": "~2.0,>=2.0.5|~3.0.0",
"symfony/dom-crawler": "~2.0,>=2.0.5|~3.0.0", "symfony/dom-crawler": "~2.0,>=2.0.5|~3.0.0",
"symfony/finder": "~2.0,>=2.0.5|~3.0.0", "symfony/finder": "~2.0,>=2.0.5|~3.0.0",
"symfony/intl": "~2.3|~3.0.0", "symfony/polyfill-intl-icu": "~1.0",
"symfony/security": "~2.6|~3.0.0", "symfony/security": "~2.6|~3.0.0",
"symfony/form": "~2.8", "symfony/form": "~2.8",
"symfony/expression-language": "~2.6|~3.0.0", "symfony/expression-language": "~2.6|~3.0.0",

View File

@ -23,7 +23,6 @@
}, },
"require-dev": { "require-dev": {
"symfony/browser-kit": "~2.4|~3.0.0", "symfony/browser-kit": "~2.4|~3.0.0",
"symfony/config": "~2.8|~3.0.0",
"symfony/console": "~2.7|~3.0.0", "symfony/console": "~2.7|~3.0.0",
"symfony/css-selector": "~2.0,>=2.0.5|~3.0.0", "symfony/css-selector": "~2.0,>=2.0.5|~3.0.0",
"symfony/dom-crawler": "~2.0,>=2.0.5|~3.0.0", "symfony/dom-crawler": "~2.0,>=2.0.5|~3.0.0",
@ -37,8 +36,7 @@
"symfony/yaml": "~2.0,>=2.0.5|~3.0.0", "symfony/yaml": "~2.0,>=2.0.5|~3.0.0",
"symfony/expression-language": "~2.6|~3.0.0", "symfony/expression-language": "~2.6|~3.0.0",
"doctrine/doctrine-bundle": "~1.2", "doctrine/doctrine-bundle": "~1.2",
"twig/twig": "~1.20|~2.0", "twig/twig": "~1.20|~2.0"
"ircmaxell/password-compat": "~1.0"
}, },
"autoload": { "autoload": {
"psr-4": { "Symfony\\Bundle\\SecurityBundle\\": "" } "psr-4": { "Symfony\\Bundle\\SecurityBundle\\": "" }

View File

@ -1077,11 +1077,7 @@ class Application
private function stringWidth($string) private function stringWidth($string)
{ {
if (!function_exists('mb_strwidth')) { if (false === $encoding = mb_detect_encoding($string, null, true)) {
return strlen($string);
}
if (false === $encoding = mb_detect_encoding($string)) {
return strlen($string); return strlen($string);
} }
@ -1094,11 +1090,7 @@ class Application
// additionally, array_slice() is not enough as some character has doubled width. // additionally, array_slice() is not enough as some character has doubled width.
// we need a function to split string not by character count but by string width // we need a function to split string not by character count but by string width
if (!function_exists('mb_strwidth')) { if (false === $encoding = mb_detect_encoding($string, null, true)) {
return str_split($string, $width);
}
if (false === $encoding = mb_detect_encoding($string)) {
return str_split($string, $width); return str_split($string, $width);
} }

View File

@ -51,11 +51,7 @@ abstract class Helper implements HelperInterface
*/ */
public static function strlen($string) public static function strlen($string)
{ {
if (!function_exists('mb_strwidth')) { if (false === $encoding = mb_detect_encoding($string, null, true)) {
return strlen($string);
}
if (false === $encoding = mb_detect_encoding($string)) {
return strlen($string); return strlen($string);
} }

View File

@ -351,7 +351,7 @@ class Table
} }
// str_pad won't work properly with multi-byte strings, we need to fix the padding // str_pad won't work properly with multi-byte strings, we need to fix the padding
if (function_exists('mb_strwidth') && false !== $encoding = mb_detect_encoding($cell)) { if (false !== $encoding = mb_detect_encoding($cell, null, true)) {
$width += strlen($cell) - mb_strwidth($cell, $encoding); $width += strlen($cell) - mb_strwidth($cell, $encoding);
} }

View File

@ -558,9 +558,6 @@ class ApplicationTest extends \PHPUnit_Framework_TestCase
$this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception4.txt', $tester->getDisplay(true), '->renderException() wraps messages when they are bigger than the terminal'); $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception4.txt', $tester->getDisplay(true), '->renderException() wraps messages when they are bigger than the terminal');
} }
/**
* @requires extension mbstring
*/
public function testRenderExceptionWithDoubleWidthCharacters() public function testRenderExceptionWithDoubleWidthCharacters()
{ {
$application = $this->getMock('Symfony\Component\Console\Application', array('getTerminalWidth')); $application = $this->getMock('Symfony\Component\Console\Application', array('getTerminalWidth'));

View File

@ -52,9 +52,6 @@ class FormatterHelperTest extends \PHPUnit_Framework_TestCase
); );
} }
/**
* @requires extension mbstring
*/
public function testFormatBlockWithDiacriticLetters() public function testFormatBlockWithDiacriticLetters()
{ {
$formatter = new FormatterHelper(); $formatter = new FormatterHelper();
@ -68,9 +65,6 @@ class FormatterHelperTest extends \PHPUnit_Framework_TestCase
); );
} }
/**
* @requires extension mbstring
*/
public function testFormatBlockWithDoubleWidthDiacriticLetters() public function testFormatBlockWithDoubleWidthDiacriticLetters()
{ {
$formatter = new FormatterHelper(); $formatter = new FormatterHelper();

View File

@ -155,9 +155,6 @@ class LegacyProgressHelperTest extends \PHPUnit_Framework_TestCase
$progress->advance(1); $progress->advance(1);
} }
/**
* @requires extension mbstring
*/
public function testMultiByteSupport() public function testMultiByteSupport()
{ {
$progress = new ProgressHelper(); $progress = new ProgressHelper();

View File

@ -256,9 +256,6 @@ TABLE
); );
} }
/**
* @requires extension mbstring
*/
public function testRenderMultiByte() public function testRenderMultiByte()
{ {
$table = new TableHelper(); $table = new TableHelper();
@ -282,9 +279,6 @@ TABLE;
$this->assertEquals($expected, $this->getOutputContent($output)); $this->assertEquals($expected, $this->getOutputContent($output));
} }
/**
* @requires extension mbstring
*/
public function testRenderFullWidthCharacters() public function testRenderFullWidthCharacters()
{ {
$table = new TableHelper(); $table = new TableHelper();

View File

@ -307,9 +307,6 @@ class ProgressBarTest extends \PHPUnit_Framework_TestCase
$bar->advance(1); $bar->advance(1);
} }
/**
* @requires extension mbstring
*/
public function testMultiByteSupport() public function testMultiByteSupport()
{ {
$bar = new ProgressBar($output = $this->getOutputStream()); $bar = new ProgressBar($output = $this->getOutputStream());
@ -541,9 +538,6 @@ class ProgressBarTest extends \PHPUnit_Framework_TestCase
); );
} }
/**
* @requires extension mbstring
*/
public function testAnsiColorsAndEmojis() public function testAnsiColorsAndEmojis()
{ {
$bar = new ProgressBar($output = $this->getOutputStream(), 15); $bar = new ProgressBar($output = $this->getOutputStream(), 15);

View File

@ -464,9 +464,6 @@ TABLE
); );
} }
/**
* @requires extension mbstring
*/
public function testRenderMultiByte() public function testRenderMultiByte()
{ {
$table = new Table($output = $this->getOutputStream()); $table = new Table($output = $this->getOutputStream());

View File

@ -16,7 +16,8 @@
} }
], ],
"require": { "require": {
"php": ">=5.3.9" "php": ">=5.3.9",
"symfony/polyfill-mbstring": "~1.0"
}, },
"require-dev": { "require-dev": {
"symfony/event-dispatcher": "~2.1|~3.0.0", "symfony/event-dispatcher": "~2.1|~3.0.0",

View File

@ -173,34 +173,7 @@ class Crawler extends \SplObjectStorage
try { try {
// Convert charset to HTML-entities to work around bugs in DOMDocument::loadHTML() // Convert charset to HTML-entities to work around bugs in DOMDocument::loadHTML()
$content = mb_convert_encoding($content, 'HTML-ENTITIES', $charset);
if (function_exists('mb_convert_encoding')) {
$content = mb_convert_encoding($content, 'HTML-ENTITIES', $charset);
} elseif (function_exists('iconv')) {
$content = preg_replace_callback(
'/[\x80-\xFF]+/',
function ($m) {
$m = unpack('C*', $m[0]);
$i = 1;
$entities = '';
while (isset($m[$i])) {
if (0xF0 <= $m[$i]) {
$c = (($m[$i++] - 0xF0) << 18) + (($m[$i++] - 0x80) << 12) + (($m[$i++] - 0x80) << 6) + $m[$i++] - 0x80;
} elseif (0xE0 <= $m[$i]) {
$c = (($m[$i++] - 0xE0) << 12) + (($m[$i++] - 0x80) << 6) + $m[$i++] - 0x80;
} else {
$c = (($m[$i++] - 0xC0) << 6) + $m[$i++] - 0x80;
}
$entities .= '&#'.$c.';';
}
return $entities;
},
iconv($charset, 'UTF-8', $content)
);
}
} catch (\Exception $e) { } catch (\Exception $e) {
} }

View File

@ -102,7 +102,6 @@ class CrawlerTest extends \PHPUnit_Framework_TestCase
/** /**
* @covers Symfony\Component\DomCrawler\Crawler::addHtmlContent * @covers Symfony\Component\DomCrawler\Crawler::addHtmlContent
* @requires extension mbstring
*/ */
public function testAddHtmlContentCharset() public function testAddHtmlContentCharset()
{ {
@ -137,7 +136,6 @@ class CrawlerTest extends \PHPUnit_Framework_TestCase
/** /**
* @covers Symfony\Component\DomCrawler\Crawler::addHtmlContent * @covers Symfony\Component\DomCrawler\Crawler::addHtmlContent
* @requires extension mbstring
*/ */
public function testAddHtmlContentCharsetGbk() public function testAddHtmlContentCharsetGbk()
{ {

View File

@ -16,7 +16,8 @@
} }
], ],
"require": { "require": {
"php": ">=5.3.9" "php": ">=5.3.9",
"symfony/polyfill-mbstring": "~1.0"
}, },
"require-dev": { "require-dev": {
"symfony/css-selector": "~2.8|~3.0.0" "symfony/css-selector": "~2.8|~3.0.0"

View File

@ -197,7 +197,7 @@ class NumberToLocalizedStringTransformer implements DataTransformerInterface
throw new TransformationFailedException('I don\'t have a clear idea what infinity looks like'); throw new TransformationFailedException('I don\'t have a clear idea what infinity looks like');
} }
if (function_exists('mb_detect_encoding') && false !== $encoding = mb_detect_encoding($value)) { if (false !== $encoding = mb_detect_encoding($value, null, true)) {
$length = mb_strlen($value, $encoding); $length = mb_strlen($value, $encoding);
$remainder = mb_substr($value, $position, $length, $encoding); $remainder = mb_substr($value, $position, $length, $encoding);
} else { } else {

View File

@ -238,8 +238,6 @@ class NumberToLocalizedStringTransformerTest extends \PHPUnit_Framework_TestCase
/** /**
* @see https://github.com/symfony/symfony/issues/7609 * @see https://github.com/symfony/symfony/issues/7609
*
* @requires extension mbstring
*/ */
public function testReverseTransformWithGroupingAndFixedSpaces() public function testReverseTransformWithGroupingAndFixedSpaces()
{ {
@ -583,7 +581,6 @@ class NumberToLocalizedStringTransformerTest extends \PHPUnit_Framework_TestCase
/** /**
* @expectedException \Symfony\Component\Form\Exception\TransformationFailedException * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException
* @expectedExceptionMessage The number contains unrecognized characters: "foo8" * @expectedExceptionMessage The number contains unrecognized characters: "foo8"
* @requires extension mbstring
*/ */
public function testReverseTransformDisallowsCenteredExtraCharactersMultibyte() public function testReverseTransformDisallowsCenteredExtraCharactersMultibyte()
{ {
@ -600,7 +597,6 @@ class NumberToLocalizedStringTransformerTest extends \PHPUnit_Framework_TestCase
/** /**
* @expectedException \Symfony\Component\Form\Exception\TransformationFailedException * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException
* @expectedExceptionMessage The number contains unrecognized characters: "foo8" * @expectedExceptionMessage The number contains unrecognized characters: "foo8"
* @requires extension mbstring
*/ */
public function testReverseTransformIgnoresTrailingSpacesInExceptionMessage() public function testReverseTransformIgnoresTrailingSpacesInExceptionMessage()
{ {
@ -628,7 +624,6 @@ class NumberToLocalizedStringTransformerTest extends \PHPUnit_Framework_TestCase
/** /**
* @expectedException \Symfony\Component\Form\Exception\TransformationFailedException * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException
* @expectedExceptionMessage The number contains unrecognized characters: "foo" * @expectedExceptionMessage The number contains unrecognized characters: "foo"
* @requires extension mbstring
*/ */
public function testReverseTransformDisallowsTrailingExtraCharactersMultibyte() public function testReverseTransformDisallowsTrailingExtraCharactersMultibyte()
{ {

View File

@ -24,7 +24,6 @@ class StringUtilTest extends \PHPUnit_Framework_TestCase
/** /**
* @dataProvider spaceProvider * @dataProvider spaceProvider
* @requires extension mbstring
*/ */
public function testTrimUtf8Separators($hex) public function testTrimUtf8Separators($hex)
{ {

View File

@ -20,6 +20,7 @@
"symfony/event-dispatcher": "~2.1|~3.0.0", "symfony/event-dispatcher": "~2.1|~3.0.0",
"symfony/intl": "~2.4|~3.0.0", "symfony/intl": "~2.4|~3.0.0",
"symfony/options-resolver": "~2.6", "symfony/options-resolver": "~2.6",
"symfony/polyfill-mbstring": "~1.0",
"symfony/property-access": "~2.3|~3.0.0" "symfony/property-access": "~2.3|~3.0.0"
}, },
"require-dev": { "require-dev": {

View File

@ -142,7 +142,7 @@ class JsonResponse extends Response
} }
if (JSON_ERROR_NONE !== json_last_error()) { if (JSON_ERROR_NONE !== json_last_error()) {
throw new \InvalidArgumentException($this->transformJsonError()); throw new \InvalidArgumentException(json_last_error_msg());
} }
$this->data = $data; $this->data = $data;
@ -196,31 +196,4 @@ class JsonResponse extends Response
return $this->setContent($this->data); return $this->setContent($this->data);
} }
private function transformJsonError()
{
if (function_exists('json_last_error_msg')) {
return json_last_error_msg();
}
switch (json_last_error()) {
case JSON_ERROR_DEPTH:
return 'Maximum stack depth exceeded.';
case JSON_ERROR_STATE_MISMATCH:
return 'Underflow or the modes mismatch.';
case JSON_ERROR_CTRL_CHAR:
return 'Unexpected control character found.';
case JSON_ERROR_SYNTAX:
return 'Syntax error, malformed JSON.';
case JSON_ERROR_UTF8:
return 'Malformed UTF-8 characters, possibly incorrectly encoded.';
default:
return 'Unknown error.';
}
}
} }

View File

@ -34,18 +34,6 @@ $response->send();
The Request and the Response classes have many other methods that implement The Request and the Response classes have many other methods that implement
the HTTP specification. the HTTP specification.
Loading
-------
If you are not using Composer but are using PHP 5.3.x, you must add the following to your autoloader:
```php
// SessionHandlerInterface
if (!interface_exists('SessionHandlerInterface')) {
$loader->registerPrefixFallback(__DIR__.'/../vendor/symfony/src/Symfony/Component/HttpFoundation/Resources/stubs');
}
```
Resources Resources
--------- ---------

View File

@ -1,102 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* SessionHandlerInterface for PHP < 5.4.
*
* The order in which these methods are invoked by PHP are:
* 1. open [session_start]
* 2. read
* 3. gc [optional depending on probability settings: gc_probability / gc_divisor]
* 4. destroy [optional when session_regenerate_id(true) is used]
* 5. write [session_write_close] or destroy [session_destroy]
* 6. close
*
* Extensive documentation can be found at php.net, see links:
*
* @see http://php.net/sessionhandlerinterface
* @see http://php.net/session.customhandler
* @see http://php.net/session-set-save-handler
*
* @author Drak <drak@zikula.org>
* @author Tobias Schultze <http://tobion.de>
*/
interface SessionHandlerInterface
{
/**
* Re-initializes existing session, or creates a new one.
*
* @see http://php.net/sessionhandlerinterface.open
*
* @param string $savePath Save path
* @param string $sessionName Session name, see http://php.net/function.session-name.php
*
* @return bool true on success, false on failure
*/
public function open($savePath, $sessionName);
/**
* Closes the current session.
*
* @see http://php.net/sessionhandlerinterface.close
*
* @return bool true on success, false on failure
*/
public function close();
/**
* Reads the session data.
*
* @see http://php.net/sessionhandlerinterface.read
*
* @param string $sessionId Session ID, see http://php.net/function.session-id
*
* @return string Same session data as passed in write() or empty string when non-existent or on failure
*/
public function read($sessionId);
/**
* Writes the session data to the storage.
*
* Care, the session ID passed to write() can be different from the one previously
* received in read() when the session ID changed due to session_regenerate_id().
*
* @see http://php.net/sessionhandlerinterface.write
*
* @param string $sessionId Session ID , see http://php.net/function.session-id
* @param string $data Serialized session data to save
*
* @return bool true on success, false on failure
*/
public function write($sessionId, $data);
/**
* Destroys a session.
*
* @see http://php.net/sessionhandlerinterface.destroy
*
* @param string $sessionId Session ID, see http://php.net/function.session-id
*
* @return bool true on success, false on failure
*/
public function destroy($sessionId);
/**
* Cleans up expired sessions (garbage collection).
*
* @see http://php.net/sessionhandlerinterface.gc
*
* @param string|int $maxlifetime Sessions that have not updated for the last maxlifetime seconds will be removed
*
* @return bool true on success, false on failure
*/
public function gc($maxlifetime);
}

View File

@ -101,11 +101,7 @@ class NativeSessionStorage implements SessionStorageInterface
session_cache_limiter(''); // disable by default because it's managed by HeaderBag (if used) session_cache_limiter(''); // disable by default because it's managed by HeaderBag (if used)
ini_set('session.use_cookies', 1); ini_set('session.use_cookies', 1);
if (PHP_VERSION_ID >= 50400) { session_register_shutdown();
session_register_shutdown();
} else {
register_shutdown_function('session_write_close');
}
$this->setMetadataBag($metaBag); $this->setMetadataBag($metaBag);
$this->setOptions($options); $this->setOptions($options);

View File

@ -16,14 +16,14 @@
} }
], ],
"require": { "require": {
"php": ">=5.3.9" "php": ">=5.3.9",
"symfony/polyfill-php54": "~1.0"
}, },
"require-dev": { "require-dev": {
"symfony/expression-language": "~2.4|~3.0.0" "symfony/expression-language": "~2.4|~3.0.0"
}, },
"autoload": { "autoload": {
"psr-4": { "Symfony\\Component\\HttpFoundation\\": "" }, "psr-4": { "Symfony\\Component\\HttpFoundation\\": "" }
"classmap": [ "Resources/stubs" ]
}, },
"minimum-stability": "dev", "minimum-stability": "dev",
"extra": { "extra": {

View File

@ -105,7 +105,7 @@ class MongoDbProfilerStorageTest extends AbstractProfilerStorageTest
$profile = new Profile('utf8_test_profile'); $profile = new Profile('utf8_test_profile');
$data = 'HЁʃʃϿ, ϢorЃd!'; $data = 'HЁʃʃϿ, ϢorЃd!';
$nonUtf8Data = mb_convert_encoding($data, 'UCS-2'); $nonUtf8Data = iconv('UTF-8', 'UCS-2', $data);
$collector = new MongoDbProfilerStorageTestDataCollector(); $collector = new MongoDbProfilerStorageTestDataCollector();
$collector->setData($nonUtf8Data); $collector->setData($nonUtf8Data);

View File

@ -30,6 +30,8 @@ use Symfony\Component\Intl\Locale\Locale;
* *
* @author Igor Wiedler <igor@wiedler.ch> * @author Igor Wiedler <igor@wiedler.ch>
* @author Bernhard Schussek <bschussek@gmail.com> * @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/ */
class Collator class Collator
{ {

View File

@ -53,37 +53,10 @@ class JsonBundleReader implements BundleReaderInterface
'The resource bundle "%s/%s.json" contains invalid JSON: %s', 'The resource bundle "%s/%s.json" contains invalid JSON: %s',
$path, $path,
$locale, $locale,
self::getLastJsonError() json_last_error_msg()
)); ));
} }
return $data; return $data;
} }
/**
* @return string The last error message created by {@link json_decode()}
*
* @link http://de2.php.net/manual/en/function.json-last-error-msg.php#113243
*/
private static function getLastJsonError()
{
if (function_exists('json_last_error_msg')) {
return json_last_error_msg();
}
static $errors = array(
JSON_ERROR_NONE => null,
JSON_ERROR_DEPTH => 'Maximum stack depth exceeded',
JSON_ERROR_STATE_MISMATCH => 'Underflow or the modes mismatch',
JSON_ERROR_CTRL_CHAR => 'Unexpected control character found',
JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON',
JSON_ERROR_UTF8 => 'Malformed UTF-8 characters, possibly incorrectly encoded',
);
$error = json_last_error();
return array_key_exists($error, $errors)
? $errors[$error]
: sprintf('Unknown error (%s)', $error);
}
} }

View File

@ -15,6 +15,8 @@ namespace Symfony\Component\Intl\DateFormatter\DateFormat;
* Parser and formatter for AM/PM markers format. * Parser and formatter for AM/PM markers format.
* *
* @author Igor Wiedler <igor@wiedler.ch> * @author Igor Wiedler <igor@wiedler.ch>
*
* @internal
*/ */
class AmPmTransformer extends Transformer class AmPmTransformer extends Transformer
{ {

View File

@ -15,6 +15,8 @@ namespace Symfony\Component\Intl\DateFormatter\DateFormat;
* Parser and formatter for day of week format. * Parser and formatter for day of week format.
* *
* @author Igor Wiedler <igor@wiedler.ch> * @author Igor Wiedler <igor@wiedler.ch>
*
* @internal
*/ */
class DayOfWeekTransformer extends Transformer class DayOfWeekTransformer extends Transformer
{ {

View File

@ -15,6 +15,8 @@ namespace Symfony\Component\Intl\DateFormatter\DateFormat;
* Parser and formatter for day of year format. * Parser and formatter for day of year format.
* *
* @author Igor Wiedler <igor@wiedler.ch> * @author Igor Wiedler <igor@wiedler.ch>
*
* @internal
*/ */
class DayOfYearTransformer extends Transformer class DayOfYearTransformer extends Transformer
{ {

View File

@ -15,6 +15,8 @@ namespace Symfony\Component\Intl\DateFormatter\DateFormat;
* Parser and formatter for day format. * Parser and formatter for day format.
* *
* @author Igor Wiedler <igor@wiedler.ch> * @author Igor Wiedler <igor@wiedler.ch>
*
* @internal
*/ */
class DayTransformer extends Transformer class DayTransformer extends Transformer
{ {

View File

@ -18,6 +18,8 @@ use Symfony\Component\Intl\Globals\IntlGlobals;
* Parser and formatter for date formats. * Parser and formatter for date formats.
* *
* @author Igor Wiedler <igor@wiedler.ch> * @author Igor Wiedler <igor@wiedler.ch>
*
* @internal
*/ */
class FullTransformer class FullTransformer
{ {

View File

@ -15,6 +15,8 @@ namespace Symfony\Component\Intl\DateFormatter\DateFormat;
* Parser and formatter for 12 hour format (0-11). * Parser and formatter for 12 hour format (0-11).
* *
* @author Igor Wiedler <igor@wiedler.ch> * @author Igor Wiedler <igor@wiedler.ch>
*
* @internal
*/ */
class Hour1200Transformer extends HourTransformer class Hour1200Transformer extends HourTransformer
{ {

View File

@ -15,6 +15,8 @@ namespace Symfony\Component\Intl\DateFormatter\DateFormat;
* Parser and formatter for 12 hour format (1-12). * Parser and formatter for 12 hour format (1-12).
* *
* @author Igor Wiedler <igor@wiedler.ch> * @author Igor Wiedler <igor@wiedler.ch>
*
* @internal
*/ */
class Hour1201Transformer extends HourTransformer class Hour1201Transformer extends HourTransformer
{ {

View File

@ -15,6 +15,8 @@ namespace Symfony\Component\Intl\DateFormatter\DateFormat;
* Parser and formatter for 24 hour format (0-23). * Parser and formatter for 24 hour format (0-23).
* *
* @author Igor Wiedler <igor@wiedler.ch> * @author Igor Wiedler <igor@wiedler.ch>
*
* @internal
*/ */
class Hour2400Transformer extends HourTransformer class Hour2400Transformer extends HourTransformer
{ {

View File

@ -15,6 +15,8 @@ namespace Symfony\Component\Intl\DateFormatter\DateFormat;
* Parser and formatter for 24 hour format (1-24). * Parser and formatter for 24 hour format (1-24).
* *
* @author Igor Wiedler <igor@wiedler.ch> * @author Igor Wiedler <igor@wiedler.ch>
*
* @internal
*/ */
class Hour2401Transformer extends HourTransformer class Hour2401Transformer extends HourTransformer
{ {

View File

@ -15,6 +15,8 @@ namespace Symfony\Component\Intl\DateFormatter\DateFormat;
* Base class for hour transformers. * Base class for hour transformers.
* *
* @author Eriksen Costa <eriksen.costa@infranology.com.br> * @author Eriksen Costa <eriksen.costa@infranology.com.br>
*
* @internal
*/ */
abstract class HourTransformer extends Transformer abstract class HourTransformer extends Transformer
{ {

View File

@ -15,6 +15,8 @@ namespace Symfony\Component\Intl\DateFormatter\DateFormat;
* Parser and formatter for minute format. * Parser and formatter for minute format.
* *
* @author Igor Wiedler <igor@wiedler.ch> * @author Igor Wiedler <igor@wiedler.ch>
*
* @internal
*/ */
class MinuteTransformer extends Transformer class MinuteTransformer extends Transformer
{ {

View File

@ -15,6 +15,8 @@ namespace Symfony\Component\Intl\DateFormatter\DateFormat;
* Parser and formatter for month format. * Parser and formatter for month format.
* *
* @author Igor Wiedler <igor@wiedler.ch> * @author Igor Wiedler <igor@wiedler.ch>
*
* @internal
*/ */
class MonthTransformer extends Transformer class MonthTransformer extends Transformer
{ {

View File

@ -15,6 +15,8 @@ namespace Symfony\Component\Intl\DateFormatter\DateFormat;
* Parser and formatter for quarter format. * Parser and formatter for quarter format.
* *
* @author Igor Wiedler <igor@wiedler.ch> * @author Igor Wiedler <igor@wiedler.ch>
*
* @internal
*/ */
class QuarterTransformer extends Transformer class QuarterTransformer extends Transformer
{ {

View File

@ -15,6 +15,8 @@ namespace Symfony\Component\Intl\DateFormatter\DateFormat;
* Parser and formatter for the second format. * Parser and formatter for the second format.
* *
* @author Igor Wiedler <igor@wiedler.ch> * @author Igor Wiedler <igor@wiedler.ch>
*
* @internal
*/ */
class SecondTransformer extends Transformer class SecondTransformer extends Transformer
{ {

View File

@ -17,6 +17,8 @@ use Symfony\Component\Intl\Exception\NotImplementedException;
* Parser and formatter for time zone format. * Parser and formatter for time zone format.
* *
* @author Igor Wiedler <igor@wiedler.ch> * @author Igor Wiedler <igor@wiedler.ch>
*
* @internal
*/ */
class TimeZoneTransformer extends Transformer class TimeZoneTransformer extends Transformer
{ {

View File

@ -15,6 +15,8 @@ namespace Symfony\Component\Intl\DateFormatter\DateFormat;
* Parser and formatter for date formats. * Parser and formatter for date formats.
* *
* @author Igor Wiedler <igor@wiedler.ch> * @author Igor Wiedler <igor@wiedler.ch>
*
* @internal
*/ */
abstract class Transformer abstract class Transformer
{ {

View File

@ -15,6 +15,8 @@ namespace Symfony\Component\Intl\DateFormatter\DateFormat;
* Parser and formatter for year format. * Parser and formatter for year format.
* *
* @author Igor Wiedler <igor@wiedler.ch> * @author Igor Wiedler <igor@wiedler.ch>
*
* @internal
*/ */
class YearTransformer extends Transformer class YearTransformer extends Transformer
{ {

View File

@ -43,6 +43,8 @@ use Symfony\Component\Intl\Locale\Locale;
* *
* @author Igor Wiedler <igor@wiedler.ch> * @author Igor Wiedler <igor@wiedler.ch>
* @author Bernhard Schussek <bschussek@gmail.com> * @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/ */
class IntlDateFormatter class IntlDateFormatter
{ {

View File

@ -15,6 +15,8 @@ namespace Symfony\Component\Intl\Globals;
* Provides fake static versions of the global functions in the intl extension. * Provides fake static versions of the global functions in the intl extension.
* *
* @author Bernhard Schussek <bschussek@gmail.com> * @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/ */
abstract class IntlGlobals abstract class IntlGlobals
{ {

View File

@ -21,6 +21,8 @@ use Symfony\Component\Intl\Exception\MethodNotImplementedException;
* *
* @author Eriksen Costa <eriksen.costa@infranology.com.br> * @author Eriksen Costa <eriksen.costa@infranology.com.br>
* @author Bernhard Schussek <bschussek@gmail.com> * @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/ */
class Locale class Locale
{ {

View File

@ -37,6 +37,8 @@ use Symfony\Component\Intl\Locale\Locale;
* *
* @author Eriksen Costa <eriksen.costa@infranology.com.br> * @author Eriksen Costa <eriksen.costa@infranology.com.br>
* @author Bernhard Schussek <bschussek@gmail.com> * @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/ */
class NumberFormatter class NumberFormatter
{ {

View File

@ -1,78 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Symfony\Component\Intl\Globals\IntlGlobals;
if (!function_exists('intl_is_failure')) {
/**
* Stub implementation for the {@link intl_is_failure()} function of the intl
* extension.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @param int $errorCode The error code returned by intl_get_error_code().
*
* @return bool Whether the error code indicates an error.
*
* @see IntlGlobals::isFailure()
*/
function intl_is_failure($errorCode)
{
return IntlGlobals::isFailure($errorCode);
}
/**
* Stub implementation for the {@link intl_get_error_code()} function of the
* intl extension.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @return bool The error code of the last intl function call or
* IntlGlobals::U_ZERO_ERROR if no error occurred.
*
* @see IntlGlobals::getErrorCode()
*/
function intl_get_error_code()
{
return IntlGlobals::getErrorCode();
}
/**
* Stub implementation for the {@link intl_get_error_code()} function of the
* intl extension.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @return bool The error message of the last intl function call or
* "U_ZERO_ERROR" if no error occurred.
*
* @see IntlGlobals::getErrorMessage()
*/
function intl_get_error_message()
{
return IntlGlobals::getErrorMessage();
}
/**
* Stub implementation for the {@link intl_error_name()} function of the intl
* extension.
*
* @param int $errorCode The error code.
*
* @return string The name of the error code constant.
*
* @see IntlGlobals::getErrorName()
*/
function intl_error_name($errorCode)
{
return IntlGlobals::getErrorName($errorCode);
}
}

View File

@ -24,7 +24,9 @@
} }
], ],
"require": { "require": {
"php": ">=5.3.9" "php": ">=5.3.9",
"symfony/polyfill-intl-icu": "~1.0",
"symfony/polyfill-php54": "~1.0"
}, },
"require-dev": { "require-dev": {
"symfony/filesystem": "~2.1|~3.0.0" "symfony/filesystem": "~2.1|~3.0.0"
@ -34,8 +36,7 @@
}, },
"autoload": { "autoload": {
"psr-4": { "Symfony\\Component\\Intl\\": "" }, "psr-4": { "Symfony\\Component\\Intl\\": "" },
"classmap": [ "Resources/stubs" ], "classmap": [ "Resources/stubs" ]
"files": [ "Resources/stubs/functions.php" ]
}, },
"minimum-stability": "dev", "minimum-stability": "dev",
"extra": { "extra": {

View File

@ -97,11 +97,7 @@ class LdapClient implements LdapClientInterface
*/ */
public function escape($subject, $ignore = '', $flags = 0) public function escape($subject, $ignore = '', $flags = 0)
{ {
if (function_exists('ldap_escape')) { return ldap_escape($subject, $ignore, $flags);
return ldap_escape($subject, $ignore, $flags);
}
return $this->doEscape($subject, $ignore, $flags);
} }
private function connect() private function connect()
@ -132,98 +128,4 @@ class LdapClient implements LdapClientInterface
$this->connection = null; $this->connection = null;
} }
/**
* Stub implementation of the {@link ldap_escape()} function of the ldap
* extension.
*
* Escape strings for safe use in LDAP filters and DNs.
*
* @author Chris Wright <ldapi@daverandom.com>
*
* @param string $subject
* @param string $ignore
* @param int $flags
*
* @return string
*
* @see http://stackoverflow.com/a/8561604
*/
private function doEscape($subject, $ignore = '', $flags = 0)
{
$charMaps = $this->getCharmaps();
// Create the base char map to escape
$flags = (int) $flags;
$charMap = array();
if ($flags & self::LDAP_ESCAPE_FILTER) {
$charMap += $charMaps[self::LDAP_ESCAPE_FILTER];
}
if ($flags & self::LDAP_ESCAPE_DN) {
$charMap += $charMaps[self::LDAP_ESCAPE_DN];
}
if (!$charMap) {
$charMap = $charMaps[0];
}
// Remove any chars to ignore from the list
$ignore = (string) $ignore;
for ($i = 0, $l = strlen($ignore); $i < $l; ++$i) {
unset($charMap[$ignore[$i]]);
}
// Do the main replacement
$result = strtr($subject, $charMap);
// Encode leading/trailing spaces if LDAP_ESCAPE_DN is passed
if ($flags & self::LDAP_ESCAPE_DN) {
if ($result[0] === ' ') {
$result = '\\20'.substr($result, 1);
}
if ($result[strlen($result) - 1] === ' ') {
$result = substr($result, 0, -1).'\\20';
}
}
return $result;
}
private function getCharmaps()
{
if (null !== $this->charmaps) {
return $this->charmaps;
}
$charMaps = array(
self::LDAP_ESCAPE_FILTER => array('\\', '*', '(', ')', "\x00"),
self::LDAP_ESCAPE_DN => array('\\', ',', '=', '+', '<', '>', ';', '"', '#'),
);
$charMaps[0] = array();
for ($i = 0; $i < 256; ++$i) {
$charMaps[0][chr($i)] = sprintf('\\%02x', $i);
}
for ($i = 0, $l = count($charMaps[self::LDAP_ESCAPE_FILTER]); $i < $l; ++$i) {
$chr = $charMaps[self::LDAP_ESCAPE_FILTER][$i];
unset($charMaps[self::LDAP_ESCAPE_FILTER][$i]);
$charMaps[self::LDAP_ESCAPE_FILTER][$chr] = $charMaps[0][$chr];
}
for ($i = 0, $l = count($charMaps[self::LDAP_ESCAPE_DN]); $i < $l; ++$i) {
$chr = $charMaps[self::LDAP_ESCAPE_DN][$i];
unset($charMaps[self::LDAP_ESCAPE_DN][$i]);
$charMaps[self::LDAP_ESCAPE_DN][$chr] = $charMaps[0][$chr];
}
$this->charmaps = $charMaps;
return $this->charmaps;
}
} }

View File

@ -1,52 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Core\Tests\Authentication\Provider;
use Symfony\Component\Ldap\LdapClient;
/**
* @requires extension ldap
*/
class LdapClientTest extends \PHPUnit_Framework_TestCase
{
/**
* @dataProvider provideLdapEscapeValues
*/
public function testLdapEscape($subject, $ignore, $flags, $expected)
{
$ldap = new LdapClient();
$this->assertSame($expected, $ldap->escape($subject, $ignore, $flags));
}
/**
* Provides values for the ldap_escape shim. These tests come from the official
* extension.
*
* @see https://github.com/php/php-src/blob/master/ext/ldap/tests/ldap_escape_dn.phpt
* @see https://github.com/php/php-src/blob/master/ext/ldap/tests/ldap_escape_all.phpt
* @see https://github.com/php/php-src/blob/master/ext/ldap/tests/ldap_escape_both.phpt
* @see https://github.com/php/php-src/blob/master/ext/ldap/tests/ldap_escape_filter.phpt
* @see https://github.com/php/php-src/blob/master/ext/ldap/tests/ldap_escape_ignore.phpt
*
* @return array
*/
public function provideLdapEscapeValues()
{
return array(
array('foo=bar(baz)*', null, LdapClient::LDAP_ESCAPE_DN, 'foo\3dbar(baz)*'),
array('foo=bar(baz)*', null, null, '\66\6f\6f\3d\62\61\72\28\62\61\7a\29\2a'),
array('foo=bar(baz)*', null, LdapClient::LDAP_ESCAPE_DN | LdapClient::LDAP_ESCAPE_FILTER, 'foo\3dbar\28baz\29\2a'),
array('foo=bar(baz)*', null, LdapClient::LDAP_ESCAPE_FILTER, 'foo=bar\28baz\29\2a'),
array('foo=bar(baz)*', 'ao', null, '\66oo\3d\62a\72\28\62a\7a\29\2a'),
);
}
}

View File

@ -17,6 +17,7 @@
], ],
"require": { "require": {
"php": ">=5.3.9", "php": ">=5.3.9",
"symfony/polyfill-php56": "~1.0",
"ext-ldap": "*" "ext-ldap": "*"
}, },
"autoload": { "autoload": {

View File

@ -34,10 +34,6 @@ class BCryptPasswordEncoder extends BasePasswordEncoder
*/ */
public function __construct($cost) public function __construct($cost)
{ {
if (!function_exists('password_hash')) {
throw new \RuntimeException('To use the BCrypt encoder, you need to upgrade to PHP 5.5 or install the "ircmaxell/password-compat" via Composer.');
}
$cost = (int) $cost; $cost = (int) $cost;
if ($cost < 4 || $cost > 31) { if ($cost < 4 || $cost > 31) {
throw new \InvalidArgumentException('Cost must be in the range of 4-31.'); throw new \InvalidArgumentException('Cost must be in the range of 4-31.');

View File

@ -11,8 +11,6 @@
namespace Symfony\Component\Security\Core\Encoder; namespace Symfony\Component\Security\Core\Encoder;
use Symfony\Component\Security\Core\Util\StringUtils;
/** /**
* BasePasswordEncoder is the base class for all password encoders. * BasePasswordEncoder is the base class for all password encoders.
* *
@ -83,7 +81,7 @@ abstract class BasePasswordEncoder implements PasswordEncoderInterface
*/ */
protected function comparePasswords($password1, $password2) protected function comparePasswords($password1, $password2)
{ {
return StringUtils::equals($password1, $password2); return hash_equals($password1, $password2);
} }
/** /**

View File

@ -64,11 +64,7 @@ class Pbkdf2PasswordEncoder extends BasePasswordEncoder
throw new \LogicException(sprintf('The algorithm "%s" is not supported.', $this->algorithm)); throw new \LogicException(sprintf('The algorithm "%s" is not supported.', $this->algorithm));
} }
if (function_exists('hash_pbkdf2')) { $digest = hash_pbkdf2($this->algorithm, $raw, $salt, $this->iterations, $this->length, true);
$digest = hash_pbkdf2($this->algorithm, $raw, $salt, $this->iterations, $this->length, true);
} else {
$digest = $this->hashPbkdf2($this->algorithm, $raw, $salt, $this->iterations, $this->length);
}
return $this->encodeHashAsBase64 ? base64_encode($digest) : bin2hex($digest); return $this->encodeHashAsBase64 ? base64_encode($digest) : bin2hex($digest);
} }
@ -80,24 +76,4 @@ class Pbkdf2PasswordEncoder extends BasePasswordEncoder
{ {
return !$this->isPasswordTooLong($raw) && $this->comparePasswords($encoded, $this->encodePassword($raw, $salt)); return !$this->isPasswordTooLong($raw) && $this->comparePasswords($encoded, $this->encodePassword($raw, $salt));
} }
private function hashPbkdf2($algorithm, $password, $salt, $iterations, $length = 0)
{
// Number of blocks needed to create the derived key
$blocks = ceil($length / strlen(hash($algorithm, null, true)));
$digest = '';
for ($i = 1; $i <= $blocks; ++$i) {
$ib = $block = hash_hmac($algorithm, $salt.pack('N', $i), $password, true);
// Iterations
for ($j = 1; $j < $iterations; ++$j) {
$ib ^= ($block = hash_hmac($algorithm, $block, $password, true));
}
$digest .= $ib;
}
return substr($digest, 0, $this->length);
}
} }

View File

@ -15,6 +15,8 @@ use Symfony\Component\Security\Core\Util\StringUtils;
/** /**
* Data from PHP.net's hash_equals tests. * Data from PHP.net's hash_equals tests.
*
* @group legacy
*/ */
class StringUtilsTest extends \PHPUnit_Framework_TestCase class StringUtilsTest extends \PHPUnit_Framework_TestCase
{ {

View File

@ -11,10 +11,16 @@
namespace Symfony\Component\Security\Core\Util; namespace Symfony\Component\Security\Core\Util;
@trigger_error('The '.__NAMESPACE__.'\\StringUtils class is deprecated since version 2.8 and will be removed in 3.0. Use hash_equals() instead.', E_USER_DEPRECATED);
use Symfony\Component\Polyfill\Util\Binary;
/** /**
* String utility functions. * String utility functions.
* *
* @author Fabien Potencier <fabien@symfony.com> * @author Fabien Potencier <fabien@symfony.com>
*
* @deprecated since 2.8, to be removed in 3.0.
*/ */
class StringUtils class StringUtils
{ {
@ -47,25 +53,7 @@ class StringUtils
$userInput = (string) $userInput; $userInput = (string) $userInput;
} }
if (function_exists('hash_equals')) { return hash_equals($knownString, $userInput);
return hash_equals($knownString, $userInput);
}
$knownLen = self::safeStrlen($knownString);
$userLen = self::safeStrlen($userInput);
if ($userLen !== $knownLen) {
return false;
}
$result = 0;
for ($i = 0; $i < $knownLen; ++$i) {
$result |= (ord($knownString[$i]) ^ ord($userInput[$i]));
}
// They are only identical strings if $result is exactly 0...
return 0 === $result;
} }
/** /**
@ -77,17 +65,6 @@ class StringUtils
*/ */
public static function safeStrlen($string) public static function safeStrlen($string)
{ {
// Premature optimization return Binary::strlen($string);
// Since this cannot be changed at runtime, we can cache it
static $funcExists = null;
if (null === $funcExists) {
$funcExists = function_exists('mb_strlen');
}
if ($funcExists) {
return mb_strlen($string, '8bit');
}
return strlen($string);
} }
} }

View File

@ -17,25 +17,25 @@
], ],
"require": { "require": {
"php": ">=5.3.9", "php": ">=5.3.9",
"paragonie/random_compat": "~1.0" "symfony/polyfill-php55": "~1.0",
"symfony/polyfill-php56": "~1.0",
"symfony/polyfill-util": "~1.0"
}, },
"require-dev": { "require-dev": {
"symfony/event-dispatcher": "~2.1|~3.0.0", "symfony/event-dispatcher": "~2.1|~3.0.0",
"symfony/expression-language": "~2.6|~3.0.0", "symfony/expression-language": "~2.6|~3.0.0",
"symfony/http-foundation": "~2.4|~3.0.0", "symfony/http-foundation": "~2.4|~3.0.0",
"symfony/ldap": "~2.8|~3.0.0",
"symfony/translation": "~2.0,>=2.0.5|~3.0.0", "symfony/translation": "~2.0,>=2.0.5|~3.0.0",
"symfony/validator": "~2.5,>=2.5.5|~3.0.0", "symfony/validator": "~2.5,>=2.5.5|~3.0.0",
"psr/log": "~1.0", "psr/log": "~1.0"
"ircmaxell/password-compat": "1.0.*",
"symfony/ldap": "~2.8|~3.0.0"
}, },
"suggest": { "suggest": {
"symfony/event-dispatcher": "", "symfony/event-dispatcher": "",
"symfony/http-foundation": "", "symfony/http-foundation": "",
"symfony/validator": "For using the user password constraint", "symfony/validator": "For using the user password constraint",
"symfony/expression-language": "For using the expression voter", "symfony/expression-language": "For using the expression voter",
"symfony/ldap": "For using LDAP integration", "symfony/ldap": "For using LDAP integration"
"ircmaxell/password-compat": "For using the BCrypt password encoder in PHP <5.5"
}, },
"autoload": { "autoload": {
"psr-4": { "Symfony\\Component\\Security\\Core\\": "" } "psr-4": { "Symfony\\Component\\Security\\Core\\": "" }

View File

@ -11,7 +11,6 @@
namespace Symfony\Component\Security\Csrf; namespace Symfony\Component\Security\Csrf;
use Symfony\Component\Security\Core\Util\StringUtils;
use Symfony\Component\Security\Csrf\TokenGenerator\UriSafeTokenGenerator; use Symfony\Component\Security\Csrf\TokenGenerator\UriSafeTokenGenerator;
use Symfony\Component\Security\Csrf\TokenGenerator\TokenGeneratorInterface; use Symfony\Component\Security\Csrf\TokenGenerator\TokenGeneratorInterface;
use Symfony\Component\Security\Csrf\TokenStorage\NativeSessionTokenStorage; use Symfony\Component\Security\Csrf\TokenStorage\NativeSessionTokenStorage;
@ -92,6 +91,6 @@ class CsrfTokenManager implements CsrfTokenManagerInterface
return false; return false;
} }
return StringUtils::equals($this->storage->getToken($token->getId()), $token->getValue()); return hash_equals($this->storage->getToken($token->getId()), $token->getValue());
} }
} }

View File

@ -17,8 +17,8 @@
], ],
"require": { "require": {
"php": ">=5.3.9", "php": ">=5.3.9",
"symfony/security-core": "~2.4|~3.0.0", "symfony/polyfill-php56": "~1.0",
"paragonie/random_compat": "~1.0" "symfony/security-core": "~2.4|~3.0.0"
}, },
"require-dev": { "require-dev": {
"symfony/http-foundation": "~2.1|~3.0.0" "symfony/http-foundation": "~2.1|~3.0.0"

View File

@ -17,7 +17,6 @@ use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\Util\StringUtils;
/** /**
* Concrete implementation of the RememberMeServicesInterface providing * Concrete implementation of the RememberMeServicesInterface providing
@ -54,7 +53,7 @@ class TokenBasedRememberMeServices extends AbstractRememberMeServices
throw new \RuntimeException(sprintf('The UserProviderInterface implementation must return an instance of UserInterface, but returned "%s".', get_class($user))); throw new \RuntimeException(sprintf('The UserProviderInterface implementation must return an instance of UserInterface, but returned "%s".', get_class($user)));
} }
if (true !== StringUtils::equals($this->generateCookieHash($class, $username, $expires, $user->getPassword()), $hash)) { if (true !== hash_equals($this->generateCookieHash($class, $username, $expires, $user->getPassword()), $hash)) {
throw new AuthenticationException('The cookie\'s hash is invalid.'); throw new AuthenticationException('The cookie\'s hash is invalid.');
} }

View File

@ -21,8 +21,9 @@
"symfony/event-dispatcher": "~2.1|~3.0.0", "symfony/event-dispatcher": "~2.1|~3.0.0",
"symfony/http-foundation": "~2.4|~3.0.0", "symfony/http-foundation": "~2.4|~3.0.0",
"symfony/http-kernel": "~2.4|~3.0.0", "symfony/http-kernel": "~2.4|~3.0.0",
"symfony/property-access": "~2.3|~3.0.0", "symfony/polyfill-php56": "~1.0",
"paragonie/random_compat": "~1.0" "symfony/polyfill-php70": "~1.0",
"symfony/property-access": "~2.3|~3.0.0"
}, },
"require-dev": { "require-dev": {
"symfony/routing": "~2.2|~3.0.0", "symfony/routing": "~2.2|~3.0.0",

View File

@ -21,8 +21,11 @@
"symfony/event-dispatcher": "~2.2|~3.0.0", "symfony/event-dispatcher": "~2.2|~3.0.0",
"symfony/http-foundation": "~2.1|~3.0.0", "symfony/http-foundation": "~2.1|~3.0.0",
"symfony/http-kernel": "~2.4|~3.0.0", "symfony/http-kernel": "~2.4|~3.0.0",
"symfony/property-access": "~2.3|~3.0.0", "symfony/polyfill-php55": "~1.0",
"paragonie/random_compat": "~1.0" "symfony/polyfill-php56": "~1.0",
"symfony/polyfill-php70": "~1.0",
"symfony/polyfill-util": "~1.0",
"symfony/property-access": "~2.3|~3.0.0"
}, },
"replace": { "replace": {
"symfony/security-core": "self.version", "symfony/security-core": "self.version",
@ -32,14 +35,13 @@
}, },
"require-dev": { "require-dev": {
"symfony/finder": "~2.3|~3.0.0", "symfony/finder": "~2.3|~3.0.0",
"symfony/intl": "~2.3|~3.0.0", "symfony/polyfill-intl-icu": "~1.0",
"symfony/routing": "~2.2|~3.0.0", "symfony/routing": "~2.2|~3.0.0",
"symfony/translation": "~2.0,>=2.0.5|~3.0.0", "symfony/translation": "~2.0,>=2.0.5|~3.0.0",
"symfony/validator": "~2.5,>=2.5.5|~3.0.0", "symfony/validator": "~2.5,>=2.5.5|~3.0.0",
"doctrine/common": "~2.2", "doctrine/common": "~2.2",
"doctrine/dbal": "~2.2", "doctrine/dbal": "~2.2",
"psr/log": "~1.0", "psr/log": "~1.0",
"ircmaxell/password-compat": "~1.0",
"symfony/expression-language": "~2.6|~3.0.0", "symfony/expression-language": "~2.6|~3.0.0",
"symfony/ldap": "~2.8|~3.0.0" "symfony/ldap": "~2.8|~3.0.0"
}, },
@ -50,7 +52,6 @@
"symfony/validator": "For using the user password constraint", "symfony/validator": "For using the user password constraint",
"symfony/routing": "For using the HttpUtils class to create sub-requests, redirect the user, and match URLs", "symfony/routing": "For using the HttpUtils class to create sub-requests, redirect the user, and match URLs",
"symfony/expression-language": "For using the expression voter", "symfony/expression-language": "For using the expression voter",
"ircmaxell/password-compat": "For using the BCrypt password encoder in PHP <5.5",
"symfony/ldap": "For using the LDAP user and authentication providers" "symfony/ldap": "For using the LDAP user and authentication providers"
}, },
"autoload": { "autoload": {

View File

@ -108,7 +108,7 @@ class JsonDecode implements DecoderInterface
} }
if (JSON_ERROR_NONE !== $this->lastError = json_last_error()) { if (JSON_ERROR_NONE !== $this->lastError = json_last_error()) {
throw new UnexpectedValueException(JsonEncoder::getLastErrorMessage()); throw new UnexpectedValueException(json_last_error_message());
} }
return $decodedData; return $decodedData;

View File

@ -56,7 +56,7 @@ class JsonEncode implements EncoderInterface
$encodedJson = json_encode($data, $context['json_encode_options']); $encodedJson = json_encode($data, $context['json_encode_options']);
if (JSON_ERROR_NONE !== $this->lastError = json_last_error()) { if (JSON_ERROR_NONE !== $this->lastError = json_last_error()) {
throw new UnexpectedValueException(JsonEncoder::getLastErrorMessage()); throw new UnexpectedValueException(json_last_error_message());
} }
return $encodedJson; return $encodedJson;

View File

@ -100,26 +100,13 @@ class JsonEncoder implements EncoderInterface, DecoderInterface
* Resolves json_last_error message. * Resolves json_last_error message.
* *
* @return string * @return string
*
* @deprecated since 2.8, to be removed in 3.0. Use json_last_error_msg() instead.
*/ */
public static function getLastErrorMessage() public static function getLastErrorMessage()
{ {
if (function_exists('json_last_error_msg')) { @trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0. Use json_last_error_msg() instead.', E_USER_DEPRECATED);
return json_last_error_msg();
}
switch (json_last_error()) { return json_last_error_msg();
case JSON_ERROR_DEPTH:
return 'Maximum stack depth exceeded';
case JSON_ERROR_STATE_MISMATCH:
return 'Underflow or the modes mismatch';
case JSON_ERROR_CTRL_CHAR:
return 'Unexpected control character found';
case JSON_ERROR_SYNTAX:
return 'Syntax error, malformed JSON';
case JSON_ERROR_UTF8:
return 'Malformed UTF-8 characters, possibly incorrectly encoded';
default:
return 'Unknown error';
}
} }
} }

View File

@ -16,7 +16,8 @@
} }
], ],
"require": { "require": {
"php": ">=5.3.9" "php": ">=5.3.9",
"symfony/polyfill-php54": "~1.0"
}, },
"require-dev": { "require-dev": {
"symfony/yaml": "~2.0,>=2.0.5|~3.0.0", "symfony/yaml": "~2.0,>=2.0.5|~3.0.0",

View File

@ -330,6 +330,9 @@ class PhpEngine implements EngineInterface, \ArrayAccess
*/ */
public function setCharset($charset) public function setCharset($charset)
{ {
if ('UTF8' === $charset = strtoupper($charset)) {
$charset = 'UTF-8'; // iconv on Windows requires "UTF-8" instead of "UTF8"
}
$this->charset = $charset; $this->charset = $charset;
foreach ($this->helpers as $helper) { foreach ($this->helpers as $helper) {
@ -448,7 +451,7 @@ class PhpEngine implements EngineInterface, \ArrayAccess
*/ */
function ($value) use ($that) { function ($value) use ($that) {
if ('UTF-8' != $that->getCharset()) { if ('UTF-8' != $that->getCharset()) {
$value = $that->convertEncoding($value, 'UTF-8', $that->getCharset()); $value = iconv($that->getCharset(), 'UTF-8', $value);
} }
$callback = function ($matches) use ($that) { $callback = function ($matches) use ($that) {
@ -460,7 +463,7 @@ class PhpEngine implements EngineInterface, \ArrayAccess
} }
// \uHHHH // \uHHHH
$char = $that->convertEncoding($char, 'UTF-16BE', 'UTF-8'); $char = iconv('UTF-8', 'UTF-16BE', $char);
return '\\u'.substr('0000'.bin2hex($char), -4); return '\\u'.substr('0000'.bin2hex($char), -4);
}; };
@ -470,7 +473,7 @@ class PhpEngine implements EngineInterface, \ArrayAccess
} }
if ('UTF-8' != $that->getCharset()) { if ('UTF-8' != $that->getCharset()) {
$value = $that->convertEncoding($value, $that->getCharset(), 'UTF-8'); $value = iconv('UTF-8', $that->getCharset(), $value);
} }
return $value; return $value;
@ -489,17 +492,13 @@ class PhpEngine implements EngineInterface, \ArrayAccess
* *
* @return string The string with the new encoding * @return string The string with the new encoding
* *
* @throws \RuntimeException if no suitable encoding function is found (iconv or mbstring) * @deprecated since 2.8, to be removed in 3.0. Use iconv() instead.
*/ */
public function convertEncoding($string, $to, $from) public function convertEncoding($string, $to, $from)
{ {
if (function_exists('mb_convert_encoding')) { @trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0. Use iconv() instead.', E_USER_DEPRECATED);
return mb_convert_encoding($string, $to, $from);
} elseif (function_exists('iconv')) {
return iconv($from, $to, $string);
}
throw new \RuntimeException('No suitable convert encoding function (use UTF-8 as your encoding or install the iconv or mbstring extension).'); return iconv($from, $to, $string);
} }
/** /**

View File

@ -137,7 +137,7 @@ class TranslationDataCollector extends DataCollector implements LateDataCollecto
{ {
$string = trim(preg_replace('/\s+/', ' ', $string)); $string = trim(preg_replace('/\s+/', ' ', $string));
if (function_exists('mb_strlen') && false !== $encoding = mb_detect_encoding($string)) { if (false !== $encoding = mb_detect_encoding($string, null, true)) {
if (mb_strlen($string, $encoding) > $length) { if (mb_strlen($string, $encoding) > $length) {
return mb_substr($string, 0, $length - 3, $encoding).'...'; return mb_substr($string, 0, $length - 3, $encoding).'...';
} }

View File

@ -153,15 +153,7 @@ class XliffFileLoader implements LoaderInterface
private function utf8ToCharset($content, $encoding = null) private function utf8ToCharset($content, $encoding = null)
{ {
if ('UTF-8' !== $encoding && !empty($encoding)) { if ('UTF-8' !== $encoding && !empty($encoding)) {
if (function_exists('mb_convert_encoding')) { return mb_convert_encoding($content, $encoding, 'UTF-8');
return mb_convert_encoding($content, $encoding, 'UTF-8');
}
if (function_exists('iconv')) {
return iconv('UTF-8', $encoding, $content);
}
throw new \RuntimeException('No suitable convert encoding function (use UTF-8 as your encoding or install the iconv or mbstring extension).');
} }
return $content; return $content;

View File

@ -16,9 +16,6 @@ use Symfony\Component\Translation\Dumper\IcuResFileDumper;
class IcuResFileDumperTest extends \PHPUnit_Framework_TestCase class IcuResFileDumperTest extends \PHPUnit_Framework_TestCase
{ {
/**
* @requires extension mbstring
*/
public function testFormatCatalogue() public function testFormatCatalogue()
{ {
$catalogue = new MessageCatalogue('en'); $catalogue = new MessageCatalogue('en');

View File

@ -59,9 +59,6 @@ class XliffFileLoaderTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(array('foo' => 'bar', 'extra' => 'extra', 'key' => '', 'test' => 'with'), $catalogue->all('domain1')); $this->assertEquals(array('foo' => 'bar', 'extra' => 'extra', 'key' => '', 'test' => 'with'), $catalogue->all('domain1'));
} }
/**
* @requires extension mbstring
*/
public function testEncoding() public function testEncoding()
{ {
$loader = new XliffFileLoader(); $loader = new XliffFileLoader();

View File

@ -16,7 +16,8 @@
} }
], ],
"require": { "require": {
"php": ">=5.3.9" "php": ">=5.3.9",
"symfony/polyfill-mbstring": "~1.0"
}, },
"require-dev": { "require-dev": {
"symfony/config": "~2.8", "symfony/config": "~2.8",

View File

@ -42,29 +42,11 @@ class LengthValidator extends ConstraintValidator
$invalidCharset = false; $invalidCharset = false;
if ('UTF8' === $charset = strtoupper($constraint->charset)) { if ('UTF8' === $charset = strtoupper($constraint->charset)) {
$charset = 'UTF-8'; $charset = 'UTF-8'; // iconv on Windows requires "UTF-8" instead of "UTF8"
} }
if ('UTF-8' === $charset) { $length = @iconv_strlen($stringValue, $charset);
if (!preg_match('//u', $stringValue)) { $invalidCharset = false === $length;
$invalidCharset = true;
} elseif (function_exists('utf8_decode')) {
$length = strlen(utf8_decode($stringValue));
} else {
preg_replace('/./u', '', $stringValue, -1, $length);
}
} elseif (function_exists('mb_strlen')) {
if (@mb_check_encoding($stringValue, $constraint->charset)) {
$length = mb_strlen($stringValue, $constraint->charset);
} else {
$invalidCharset = true;
}
} elseif (function_exists('iconv_strlen')) {
$length = @iconv_strlen($stringValue, $constraint->charset);
$invalidCharset = false === $length;
} else {
$length = strlen($stringValue);
}
if ($invalidCharset) { if ($invalidCharset) {
if ($this->context instanceof ExecutionContextInterface) { if ($this->context instanceof ExecutionContextInterface) {

View File

@ -87,9 +87,6 @@ class LengthValidatorTest extends AbstractConstraintValidatorTest
); );
} }
/**
* @requires extension mbstring
*/
public function getOneCharset() public function getOneCharset()
{ {
return array( return array(

View File

@ -48,7 +48,7 @@ class CutStub extends Stub
case 'string': case 'string':
$this->type = self::TYPE_STRING; $this->type = self::TYPE_STRING;
$this->class = preg_match('//u', $value) ? self::STRING_UTF8 : self::STRING_BINARY; $this->class = preg_match('//u', $value) ? self::STRING_UTF8 : self::STRING_BINARY;
$this->cut = self::STRING_BINARY === $this->class ? strlen($value) : (function_exists('iconv_strlen') ? iconv_strlen($value, 'UTF-8') : -1); $this->cut = self::STRING_BINARY === $this->class ? strlen($value) : mb_strlen($value, 'UTF-8');
$this->value = ''; $this->value = '';
break; break;
} }

View File

@ -185,9 +185,6 @@ abstract class AbstractCloner implements ClonerInterface
$this->filter = $filter; $this->filter = $filter;
$this->prevErrorHandler = set_error_handler(array($this, 'handleError')); $this->prevErrorHandler = set_error_handler(array($this, 'handleError'));
try { try {
if (!function_exists('iconv')) {
$this->maxString = -1;
}
$data = $this->doClone($var); $data = $this->doClone($var);
} catch (\Exception $e) { } catch (\Exception $e) {
} }

View File

@ -30,7 +30,6 @@ abstract class AbstractDumper implements DataDumperInterface, DumperInterface
protected $indentPad = ' '; protected $indentPad = ' ';
private $charset; private $charset;
private $charsetConverter;
/** /**
* @param callable|resource|string|null $output A line dumper callable, an opened stream or an output path, defaults to static::$defaultOutput. * @param callable|resource|string|null $output A line dumper callable, an opened stream or an output path, defaults to static::$defaultOutput.
@ -82,29 +81,11 @@ abstract class AbstractDumper implements DataDumperInterface, DumperInterface
public function setCharset($charset) public function setCharset($charset)
{ {
$prev = $this->charset; $prev = $this->charset;
$this->charsetConverter = 'fallback';
$charset = strtoupper($charset); $charset = strtoupper($charset);
$charset = null === $charset || 'UTF-8' === $charset || 'UTF8' === $charset ? 'CP1252' : $charset; $charset = null === $charset || 'UTF-8' === $charset || 'UTF8' === $charset ? 'CP1252' : $charset;
$supported = true; $this->charset = $charset;
set_error_handler(function () use (&$supported) {$supported = false;});
if (function_exists('mb_encoding_aliases') && mb_encoding_aliases($charset)) {
$this->charset = $charset;
$this->charsetConverter = 'mbstring';
} elseif (function_exists('iconv')) {
$supported = true;
iconv($charset, 'UTF-8', '');
if ($supported) {
$this->charset = $charset;
$this->charsetConverter = 'iconv';
}
}
if ('fallback' === $this->charsetConverter) {
$this->charset = 'ISO-8859-1';
}
restore_error_handler();
return $prev; return $prev;
} }
@ -183,40 +164,13 @@ abstract class AbstractDumper implements DataDumperInterface, DumperInterface
*/ */
protected function utf8Encode($s) protected function utf8Encode($s)
{ {
if ('mbstring' === $this->charsetConverter) { if (false !== $c = @iconv($this->charset, 'UTF-8', $s)) {
return mb_convert_encoding($s, 'UTF-8', mb_check_encoding($s, $this->charset) ? $this->charset : '8bit'); return $c;
} }
if ('iconv' === $this->charsetConverter) { if ('CP1252' !== $this->charset && false !== $c = @iconv('CP1252', 'UTF-8', $s)) {
$valid = true; return $c;
set_error_handler(function () use (&$valid) {$valid = false;});
$c = iconv($this->charset, 'UTF-8', $s);
restore_error_handler();
if ($valid) {
return $c;
}
} }
$s .= $s; return iconv('CP850', 'UTF-8', $s);
$len = strlen($s);
for ($i = $len >> 1, $j = 0; $i < $len; ++$i, ++$j) {
switch (true) {
case $s[$i] < "\x80":
$s[$j] = $s[$i];
break;
case $s[$i] < "\xC0":
$s[$j] = "\xC2";
$s[++$j] = $s[$i];
break;
default:
$s[$j] = "\xC3";
$s[++$j] = chr(ord($s[$i]) - 64);
break;
}
}
return substr($s, 0, $j);
} }
} }

View File

@ -91,9 +91,7 @@ class CliDumper extends AbstractDumper
*/ */
public function setMaxStringWidth($maxStringWidth) public function setMaxStringWidth($maxStringWidth)
{ {
if (function_exists('iconv')) { $this->maxStringWidth = (int) $maxStringWidth;
$this->maxStringWidth = (int) $maxStringWidth;
}
} }
/** /**
@ -171,7 +169,7 @@ class CliDumper extends AbstractDumper
$this->dumpLine($cursor->depth, true); $this->dumpLine($cursor->depth, true);
} else { } else {
$attr = array( $attr = array(
'length' => 0 <= $cut && function_exists('iconv_strlen') ? iconv_strlen($str, 'UTF-8') + $cut : 0, 'length' => 0 <= $cut ? iconv_strlen($str, 'UTF-8') + $cut : 0,
'binary' => $bin, 'binary' => $bin,
); );
$str = explode("\n", $str); $str = explode("\n", $str);

View File

@ -442,30 +442,7 @@ EOHTML;
} }
$this->lastDepth = $depth; $this->lastDepth = $depth;
// Replaces non-ASCII UTF-8 chars by numeric HTML entities $this->line = mb_convert_encoding($this->line, 'HTML-ENTITIES', 'UTF-8');
$this->line = preg_replace_callback(
'/[\x80-\xFF]+/',
function ($m) {
$m = unpack('C*', $m[0]);
$i = 1;
$entities = '';
while (isset($m[$i])) {
if (0xF0 <= $m[$i]) {
$c = (($m[$i++] - 0xF0) << 18) + (($m[$i++] - 0x80) << 12) + (($m[$i++] - 0x80) << 6) + $m[$i++] - 0x80;
} elseif (0xE0 <= $m[$i]) {
$c = (($m[$i++] - 0xE0) << 12) + (($m[$i++] - 0x80) << 6) + $m[$i++] - 0x80;
} else {
$c = (($m[$i++] - 0xC0) << 6) + $m[$i++] - 0x80;
}
$entities .= '&#'.$c.';';
}
return $entities;
},
$this->line
);
if (-1 === $depth) { if (-1 === $depth) {
AbstractDumper::dumpLine(0); AbstractDumper::dumpLine(0);

View File

@ -52,7 +52,7 @@ class HtmlDumperTest extends \PHPUnit_Framework_TestCase
$closure54 = <<<EOTXT $closure54 = <<<EOTXT
<span class=sf-dump-meta>class</span>: "<span class=sf-dump-str title="48 characters">Symfony\Component\VarDumper\Tests\HtmlDumperTest</span>" <span class=sf-dump-meta>class</span>: "<span class=sf-dump-str title="48 characters">Symfony\Component\VarDumper\Tests\HtmlDumperTest</span>"
<span class=sf-dump-meta>this</span>: <abbr title="Symfony\Component\VarDumper\Tests\HtmlDumperTest" class=sf-dump-note>HtmlDumperTest</abbr> {{$r} &#8230;} <span class=sf-dump-meta>this</span>: <abbr title="Symfony\Component\VarDumper\Tests\HtmlDumperTest" class=sf-dump-note>HtmlDumperTest</abbr> {{$r} &%s;}
EOTXT; EOTXT;
} }
@ -68,8 +68,8 @@ EOTXT;
<span class=sf-dump-key>4</span> => <span class=sf-dump-num>INF</span> <span class=sf-dump-key>4</span> => <span class=sf-dump-num>INF</span>
<span class=sf-dump-key>5</span> => <span class=sf-dump-num>-INF</span> <span class=sf-dump-key>5</span> => <span class=sf-dump-num>-INF</span>
<span class=sf-dump-key>6</span> => <span class=sf-dump-num>{$intMax}</span> <span class=sf-dump-key>6</span> => <span class=sf-dump-num>{$intMax}</span>
"<span class=sf-dump-key>str</span>" => "<span class=sf-dump-str title="5 characters">d&#233;j&#224;</span>\\n" "<span class=sf-dump-key>str</span>" => "<span class=sf-dump-str title="5 characters">d&%s;j&%s;</span>\\n"
<span class=sf-dump-key>7</span> => b"<span class=sf-dump-str title="2 binary or non-UTF-8 characters">&#233;</span>\\x00" <span class=sf-dump-key>7</span> => b"<span class=sf-dump-str title="2 binary or non-UTF-8 characters">&%s;</span>\\x00"
"<span class=sf-dump-key>[]</span>" => [] "<span class=sf-dump-key>[]</span>" => []
"<span class=sf-dump-key>res</span>" => <span class=sf-dump-note>stream resource</span> <a class=sf-dump-ref>@{$res}</a><samp> "<span class=sf-dump-key>res</span>" => <span class=sf-dump-note>stream resource</span> <a class=sf-dump-ref>@{$res}</a><samp>
<span class=sf-dump-meta>wrapper_type</span>: "<span class=sf-dump-str title="9 characters">plainfile</span>" <span class=sf-dump-meta>wrapper_type</span>: "<span class=sf-dump-str title="9 characters">plainfile</span>"
@ -109,7 +109,7 @@ EOTXT;
"<span class=sf-dump-key>snobj</span>" => <a class=sf-dump-ref href=#{$dumpId}-ref03 title="2 occurrences">&amp;3</a> {<a class=sf-dump-ref href=#{$dumpId}-ref2%d title="3 occurrences">#%d</a>} "<span class=sf-dump-key>snobj</span>" => <a class=sf-dump-ref href=#{$dumpId}-ref03 title="2 occurrences">&amp;3</a> {<a class=sf-dump-ref href=#{$dumpId}-ref2%d title="3 occurrences">#%d</a>}
"<span class=sf-dump-key>snobj2</span>" => {<a class=sf-dump-ref href=#{$dumpId}-ref2%d title="3 occurrences">#%d</a>} "<span class=sf-dump-key>snobj2</span>" => {<a class=sf-dump-ref href=#{$dumpId}-ref2%d title="3 occurrences">#%d</a>}
"<span class=sf-dump-key>file</span>" => "<span class=sf-dump-str title="%d characters">{$var['file']}</span>" "<span class=sf-dump-key>file</span>" => "<span class=sf-dump-str title="%d characters">{$var['file']}</span>"
b"<span class=sf-dump-key>bin-key-&#233;</span>" => "" b"<span class=sf-dump-key>bin-key-&%s;</span>" => ""
</samp>] </samp>]
</bar> </bar>
@ -120,9 +120,6 @@ EOTXT
); );
} }
/**
* @requires extension mbstring
*/
public function testCharset() public function testCharset()
{ {
$var = mb_convert_encoding('Словарь', 'CP1251', 'UTF-8'); $var = mb_convert_encoding('Словарь', 'CP1251', 'UTF-8');

View File

@ -16,7 +16,8 @@
} }
], ],
"require": { "require": {
"php": ">=5.3.9" "php": ">=5.3.9",
"symfony/polyfill-mbstring": "~1.0"
}, },
"require-dev": { "require-dev": {
"twig/twig": "~1.20|~2.0" "twig/twig": "~1.20|~2.0"

View File

@ -52,7 +52,7 @@ class Inline
return ''; return '';
} }
if (function_exists('mb_internal_encoding') && ((int) ini_get('mbstring.func_overload')) & 2) { if (2 /* MB_OVERLOAD_STRING */ & (int) ini_get('mbstring.func_overload')) {
$mbEncoding = mb_internal_encoding(); $mbEncoding = mb_internal_encoding();
mb_internal_encoding('ASCII'); mb_internal_encoding('ASCII');
} }

View File

@ -62,7 +62,7 @@ class Parser
$value = $this->cleanup($value); $value = $this->cleanup($value);
$this->lines = explode("\n", $value); $this->lines = explode("\n", $value);
if (function_exists('mb_internal_encoding') && ((int) ini_get('mbstring.func_overload')) & 2) { if (2 /* MB_OVERLOAD_STRING */ & (int) ini_get('mbstring.func_overload')) {
$mbEncoding = mb_internal_encoding(); $mbEncoding = mb_internal_encoding();
mb_internal_encoding('UTF-8'); mb_internal_encoding('UTF-8');
} }