diff --git a/CHANGELOG-2.2.md b/CHANGELOG-2.2.md index 74f7e2c303..8abd730929 100644 --- a/CHANGELOG-2.2.md +++ b/CHANGELOG-2.2.md @@ -7,6 +7,62 @@ in 2.2 minor versions. To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v2.2.0...v2.2.1 +* 2.2.1 (2013-04-06) + + * 751abe1: Doctrine cannot handle bare random non-utf8 strings + * 673fd9b: idAsIndex should be true with a smallint or bigint id field. + * 64a1d39: Fixed long multibyte parameter logging in DbalLogger:startQuery + * 4cf06c1: Keep the file extension in the temporary copy and test that it exists (closes #7482) + * 64ac34d: [Security] fixed wrong interface + * 9875c4b: Added '@@' escaping strategy for YamlFileLoader and YamlDumper + * bbcdfe2: [Yaml] fixed bugs with folded scalar parsing + * 5afea04: [Form] made DefaultCsrfProvider using session_status() when available + * c928ddc: [HttpFoudantion] fixed Request::getPreferredLanguage() + * e6b7515: [DomCrawler] added support for query string with slash + * 633c051: Fixed invalid file path for hiddeninput.exe on Windows. + * 7ef90d2: fix xsd definition for strict-requirements + * 39445c5: [WebProfilerBundle] Fixed the toolbar styles to apply them in IE8 + * 601da45: [ClassLoader] fixed heredocs handling + * 17dc2ff: [HttpRequest] fixes Request::getLanguages() bug + * 67fbbac: [DoctrineBridge] Fixed non-utf-8 recognition + * e51432a: sub-requests are now created with the same class as their parent + * cc3a40e: [FrameworkBundle] changed temp kernel name in cache:clear + * d7a7434: [Routing] fix url generation for optional parameter having a null value + * ef53456: [DoctrineBridge] Avoids blob values to be logged by doctrine + * 6575df6: [Security] use current request attributes to generate redirect url? + * 7216cb0: [Validator] fix showing wrong max file size for upload errors + * c423f16: [2.1][TwigBridge] Fixes Issue #7342 in TwigBridge + * 7d87ecd: [FrameworkBundle] fixed cahe:clear command's warmup + * 5ad4bd1: [TwigBridge] now enter/leave scope on Twig_Node_Module + * fe4cc24: [TwigBridge] fixed fixed scope & trans_default_domain node visitor + * fc47589: [BrowserKit] added ability to ignored malformed set-cookie header + * 602cdee: replace INF to PHP_INT_MAX inside Finder component. + * 5bc30bb: [Translation] added xliff loader/dumper with resname support + * 663c796: Property accessor custom array object fix + * 4f3771d: [2.2][HttpKernel] fixed wrong option name in FragmentHandler::fixOptions + * a735cbd: fix xargs pipe to work with spaces in dir names + * 15bf033: [FrameworkBundle] fix router debug command + * d16d193: [FramworkBundle] removed unused property of trans update command + * 523ef29: Fix warning for buildXml method + * 7241be9: [Finder] fixed a potential issue on Solaris where INF value is wrong (refs #7269) + * 1d3da29: [FrameworkBundle] avoids cache:clear to break if new/old folders already exist + * b9cdb9a: [HttpKernel] Fixed possible profiler token collision (closes #7272, closes #7171) + * d1f5d25: [FrameworkBundle] Fixes invalid serialized objects in cache + * c82c754: RedisProfilerStorage wrong db-number/index-number selected + * e86fefa: Unset loading[$id] in ContainerBuilder on exception + * 709518b: Default validation message translation fix. + * c0687cd: remove() should not use deprecated getParent() so it does not trigger deprecation internally + * 708c0d3: adjust routing tests to not use prefix in addCollection + * acff735: [Routing] trigger deprecation warning for deprecated features that will be removed in 2.3 + * 41ad9d8: [Routing] make xml loader more tolerant + * 73bead7: [ClassLoader] made DebugClassLoader idempotent + * a4ec677: [DomCrawler] Fix relative path handling in links + * 6681df0: [Console] fixed StringInput binding + * 5bf2f71: [Console] added deprecation annotation + * 8d9cd42: Routing issue with installation in a sub-directory ref: https://github.com/symfony/symfony/issues/7129 + * c97ee8d: [Translator] mention that the message id may also be an object that can be cast to string in TranslatorInterface and fix the IdentityTranslator that did not respect this + * 5a36b2d: [Translator] fix MessageCatalogueInterface::getFallbackCatalogue that can return null + * 2.2.0 (2013-03-01) * 5b19c89: [Console] fixed unparsed StringInput tokens diff --git a/src/Symfony/Bridge/Twig/Extension/HttpKernelExtension.php b/src/Symfony/Bridge/Twig/Extension/HttpKernelExtension.php index 6aadbb7e01..a837723430 100644 --- a/src/Symfony/Bridge/Twig/Extension/HttpKernelExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/HttpKernelExtension.php @@ -45,8 +45,8 @@ class HttpKernelExtension extends \Twig_Extension /** * Renders a fragment. * - * @param string $uri A URI - * @param array $options An array of options + * @param string|ControllerReference $uri A URI as a string or a ControllerReference instance + * @param array $options An array of options * * @return string The fragment content * @@ -63,9 +63,9 @@ class HttpKernelExtension extends \Twig_Extension /** * Renders a fragment. * - * @param string $strategy A strategy name - * @param string $uri A URI - * @param array $options An array of options + * @param string $strategy A strategy name + * @param string|ControllerReference $uri A URI as a string or a ControllerReference instance + * @param array $options An array of options * * @return string The fragment content * diff --git a/src/Symfony/Bundle/FrameworkBundle/Test/WebTestCase.php b/src/Symfony/Bundle/FrameworkBundle/Test/WebTestCase.php index 1011e1c1b0..37cf41c8eb 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Test/WebTestCase.php +++ b/src/Symfony/Bundle/FrameworkBundle/Test/WebTestCase.php @@ -84,19 +84,19 @@ abstract class WebTestCase extends \PHPUnit_Framework_TestCase } /** - * Finds the value of configuration flag from cli + * Finds the value of the CLI configuration option. * * PHPUnit will use the last configuration argument on the command line, so this only returns - * the last configuration argument + * the last configuration argument. * - * @return string The value of the phpunit cli configuration option + * @return string The value of the PHPUnit cli configuration option */ private static function getPhpUnitCliConfigArgument() { $dir = null; $reversedArgs = array_reverse($_SERVER['argv']); foreach ($reversedArgs as $argIndex => $testArg) { - if ($testArg === '-c' || $testArg === '--configuration') { + if (preg_match('/^-[^ \-]*c$/', $testArg) || $testArg === '--configuration') { $dir = realpath($reversedArgs[$argIndex - 1]); break; } elseif (strpos($testArg, '--configuration=') === 0) { diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/request.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/request.html.twig index 44a255000c..9c0ea3fb5e 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/request.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/request.html.twig @@ -9,7 +9,7 @@ {{ collector.controller.method }} {% else %} - {{ collector.controller }} + {{ collector.controller }} {% endif %} {% endset %} {% set request_status_code_color = (400 > collector.statuscode) ? ((200 == collector.statuscode) ? 'green' : 'yellow') : 'red'%} diff --git a/src/Symfony/Component/Console/Application.php b/src/Symfony/Component/Console/Application.php index 16f16a33d9..4aec01637f 100644 --- a/src/Symfony/Component/Console/Application.php +++ b/src/Symfony/Component/Console/Application.php @@ -503,20 +503,24 @@ class Application */ public function findNamespace($namespace) { - $allNamespaces = array(); - foreach ($this->getNamespaces() as $n) { - $allNamespaces[$n] = explode(':', $n); - } - - $found = array(); + $allNamespaces = $this->getNamespaces(); + $found = ''; foreach (explode(':', $namespace) as $i => $part) { - $abbrevs = static::getAbbreviations(array_unique(array_values(array_filter(array_map(function ($p) use ($i) { return isset($p[$i]) ? $p[$i] : ''; }, $allNamespaces))))); + // select sub-namespaces matching the current namespace we found + $namespaces = array(); + foreach ($allNamespaces as $n) { + if ('' === $found || 0 === strpos($n, $found)) { + $namespaces[$n] = explode(':', $n); + } + } + + $abbrevs = static::getAbbreviations(array_unique(array_values(array_filter(array_map(function ($p) use ($i) { return isset($p[$i]) ? $p[$i] : ''; }, $namespaces))))); if (!isset($abbrevs[$part])) { $message = sprintf('There are no commands defined in the "%s" namespace.', $namespace); if (1 <= $i) { - $part = implode(':', $found).':'.$part; + $part = $found.':'.$part; } if ($alternatives = $this->findAlternativeNamespace($part, $abbrevs)) { @@ -532,14 +536,19 @@ class Application throw new \InvalidArgumentException($message); } + // there are multiple matches, but $part is an exact match of one of them so we select it + if (in_array($part, $abbrevs[$part])) { + $abbrevs[$part] = array($part); + } + if (count($abbrevs[$part]) > 1) { throw new \InvalidArgumentException(sprintf('The namespace "%s" is ambiguous (%s).', $namespace, $this->getAbbreviationSuggestions($abbrevs[$part]))); } - $found[] = $abbrevs[$part][0]; + $found .= $found ? ':' . $abbrevs[$part][0] : $abbrevs[$part][0]; } - return implode(':', $found); + return $found; } /** @@ -662,21 +671,12 @@ class Application { $abbrevs = array(); foreach ($names as $name) { - for ($len = strlen($name) - 1; $len > 0; --$len) { + for ($len = strlen($name); $len > 0; --$len) { $abbrev = substr($name, 0, $len); - if (!isset($abbrevs[$abbrev])) { - $abbrevs[$abbrev] = array($name); - } else { - $abbrevs[$abbrev][] = $name; - } + $abbrevs[$abbrev][] = $name; } } - // Non-abbreviations always get entered, even if they aren't unique - foreach ($names as $name) { - $abbrevs[$name] = array($name); - } - return $abbrevs; } diff --git a/src/Symfony/Component/Console/Tests/ApplicationTest.php b/src/Symfony/Component/Console/Tests/ApplicationTest.php index 9494211135..9650969592 100644 --- a/src/Symfony/Component/Console/Tests/ApplicationTest.php +++ b/src/Symfony/Component/Console/Tests/ApplicationTest.php @@ -351,6 +351,16 @@ class ApplicationTest extends \PHPUnit_Framework_TestCase } } + public function testFindNamespaceDoesNotFailOnDeepSimilarNamespaces() + { + $application = $this->getMock('Symfony\Component\Console\Application', array('getNamespaces')); + $application->expects($this->once()) + ->method('getNamespaces') + ->will($this->returnValue(array('foo:sublong', 'bar:sub'))); + + $this->assertEquals('foo:sublong', $application->findNamespace('f:sub')); + } + public function testSetCatchExceptions() { $application = $this->getMock('Symfony\Component\Console\Application', array('getTerminalWidth')); diff --git a/src/Symfony/Component/Debug/ErrorHandler.php b/src/Symfony/Component/Debug/ErrorHandler.php index b0843c1b01..facb7c8fe3 100644 --- a/src/Symfony/Component/Debug/ErrorHandler.php +++ b/src/Symfony/Component/Debug/ErrorHandler.php @@ -87,7 +87,17 @@ class ErrorHandler if ($level & (E_USER_DEPRECATED | E_DEPRECATED)) { if (null !== self::$logger) { - $stack = version_compare(PHP_VERSION, '5.4', '<') ? array_slice(debug_backtrace(false), 0, 10) : debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 10); + if (version_compare(PHP_VERSION, '5.4', '<')) { + $stack = array_map( + function ($row) { + unset($row['args']); + return $row; + }, + array_slice(debug_backtrace(false), 0, 10) + ); + } else { + $stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 10); + } self::$logger->warning($message, array('type' => self::TYPE_DEPRECATION, 'stack' => $stack)); } diff --git a/src/Symfony/Component/HttpFoundation/Session/Attribute/AttributeBag.php b/src/Symfony/Component/HttpFoundation/Session/Attribute/AttributeBag.php index 2f1a4222e7..e9d0257152 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Attribute/AttributeBag.php +++ b/src/Symfony/Component/HttpFoundation/Session/Attribute/AttributeBag.php @@ -31,7 +31,7 @@ class AttributeBag implements AttributeBagInterface, \IteratorAggregate, \Counta /** * Constructor. * - * @param string $storageKey The key used to store flashes in the session. + * @param string $storageKey The key used to store attributes in the session. */ public function __construct($storageKey = '_sf2_attributes') { diff --git a/src/Symfony/Component/HttpKernel/Config/FileLocator.php b/src/Symfony/Component/HttpKernel/Config/FileLocator.php index d241b9da19..47b543c15e 100644 --- a/src/Symfony/Component/HttpKernel/Config/FileLocator.php +++ b/src/Symfony/Component/HttpKernel/Config/FileLocator.php @@ -28,14 +28,16 @@ class FileLocator extends BaseFileLocator * Constructor. * * @param KernelInterface $kernel A KernelInterface instance - * @param string $path The path the global resource directory - * @param string|array $paths A path or an array of paths where to look for resources + * @param null|string $path The path the global resource directory + * @param array $paths An array of paths where to look for resources */ public function __construct(KernelInterface $kernel, $path = null, array $paths = array()) { $this->kernel = $kernel; - $this->path = $path; - $paths[] = $path; + if (null !== $path) { + $this->path = $path; + $paths[] = $path; + } parent::__construct($paths); } diff --git a/src/Symfony/Component/HttpKernel/Tests/Config/FileLocatorTest.php b/src/Symfony/Component/HttpKernel/Tests/Config/FileLocatorTest.php new file mode 100755 index 0000000000..be59486269 --- /dev/null +++ b/src/Symfony/Component/HttpKernel/Tests/Config/FileLocatorTest.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Config; + +use Symfony\Component\HttpKernel\Config\FileLocator; + +class FileLocatorTest extends \PHPUnit_Framework_TestCase +{ + public function testLocate() + { + $kernel = $this->getMock('Symfony\Component\HttpKernel\KernelInterface'); + $kernel + ->expects($this->atLeastOnce()) + ->method('locateResource') + ->with('@BundleName/some/path', null, true) + ->will($this->returnValue('/bundle-name/some/path')); + $locator = new FileLocator($kernel); + $this->assertEquals('/bundle-name/some/path', $locator->locate('@BundleName/some/path')); + + $kernel + ->expects($this->never()) + ->method('locateResource'); + $this->setExpectedException('LogicException'); + $locator->locate('/some/path'); + } + + public function testLocateWithGlobalResourcePath() + { + $kernel = $this->getMock('Symfony\Component\HttpKernel\KernelInterface'); + $kernel + ->expects($this->atLeastOnce()) + ->method('locateResource') + ->with('@BundleName/some/path', '/global/resource/path', false); + + $locator = new FileLocator($kernel, '/global/resource/path'); + $locator->locate('@BundleName/some/path', null, false); + } +} diff --git a/src/Symfony/Component/HttpKernel/Tests/EventListener/LocaleListenerTest.php b/src/Symfony/Component/HttpKernel/Tests/EventListener/LocaleListenerTest.php index 9ca64321b2..e5e4e3a286 100644 --- a/src/Symfony/Component/HttpKernel/Tests/EventListener/LocaleListenerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/EventListener/LocaleListenerTest.php @@ -68,6 +68,17 @@ class LocaleListenerTest extends \PHPUnit_Framework_TestCase $listener->onKernelRequest($this->getEvent($request)); } + public function testRequestLocaleIsNotOverridden() + { + $request = Request::create('/'); + $request->setLocale('de'); + $listener = new LocaleListener('fr'); + $event = $this->getEvent($request); + + $listener->onKernelRequest($event); + $this->assertEquals('de', $request->getLocale()); + } + private function getEvent(Request $request) { return new GetResponseEvent($this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'), $request, HttpKernelInterface::MASTER_REQUEST); diff --git a/src/Symfony/Component/Process/Process.php b/src/Symfony/Component/Process/Process.php index e60373d57b..0cfd371f6e 100644 --- a/src/Symfony/Component/Process/Process.php +++ b/src/Symfony/Component/Process/Process.php @@ -35,10 +35,14 @@ class Process const STDOUT = 1; const STDERR = 2; + // Timeout Precision in seconds. + const TIMEOUT_PRECISION = 0.2; + private $commandline; private $cwd; private $env; private $stdin; + private $starttime; private $timeout; private $options; private $exitcode; @@ -228,6 +232,7 @@ class Process throw new RuntimeException('Process is already running'); } + $this->starttime = microtime(true); $this->stdout = ''; $this->stderr = ''; $this->incrementalOutputOffset = 0; @@ -304,7 +309,7 @@ class Process $w = $writePipes; $e = null; - $n = @stream_select($r, $w, $e, $this->timeout); + $n = @stream_select($r, $w, $e, 0, ceil(static::TIMEOUT_PRECISION * 1E6)); if (false === $n) { break; @@ -337,6 +342,8 @@ class Process unset($this->pipes[$type]); } } + + $this->checkTimeout(); } $this->updateStatus(); @@ -360,7 +367,7 @@ class Process public function restart($callback = null) { if ($this->isRunning()) { - throw new \RuntimeException('Process is already running'); + throw new RuntimeException('Process is already running'); } $process = clone $this; @@ -391,13 +398,15 @@ class Process if (defined('PHP_WINDOWS_VERSION_BUILD') && $this->fileHandles) { $this->processFileHandles($callback, !$this->pipes); } + $this->checkTimeout(); if ($this->pipes) { $r = $this->pipes; $w = null; $e = null; - if (false === $n = @stream_select($r, $w, $e, $this->timeout)) { + // let's have a look if something changed in streams + if (false === $n = @stream_select($r, $w, $e, 0, ceil(static::TIMEOUT_PRECISION * 1E6))) { $lastError = error_get_last(); // stream_select returns false when the `select` system call is interrupted by an incoming signal @@ -407,10 +416,10 @@ class Process continue; } - if (0 === $n) { - proc_terminate($this->process); - throw new RuntimeException('The process timed out.'); + // nothing has changed + if (0 === $n) { + continue; } foreach ($r as $pipe) { @@ -712,7 +721,7 @@ class Process */ public function stop($timeout = 10) { - $timeoutMicro = (int) $timeout*10E6; + $timeoutMicro = (int) $timeout*1E6; if ($this->isRunning()) { proc_terminate($this->process); $time = 0; @@ -721,6 +730,10 @@ class Process usleep(1000); } + if (!defined('PHP_WINDOWS_VERSION_BUILD') && $this->isRunning()) { + proc_terminate($this->process, SIGKILL); + } + foreach ($this->pipes as $pipe) { fclose($pipe); } @@ -800,7 +813,7 @@ class Process * * To disable the timeout, set this value to null. * - * @param integer|null $timeout The timeout in seconds + * @param float|null $timeout The timeout in seconds * * @return self The current Process instance * @@ -814,10 +827,10 @@ class Process return $this; } - $timeout = (integer) $timeout; + $timeout = (float) $timeout; if ($timeout < 0) { - throw new InvalidArgumentException('The timeout value must be a valid positive integer.'); + throw new InvalidArgumentException('The timeout value must be a valid positive integer or float number.'); } $this->timeout = $timeout; @@ -982,6 +995,24 @@ class Process return $this; } + /** + * Performs a check between the timeout definition and the time the process + * started + * + * In case you run a background process (with the start method), you should + * trigger this method regularly to ensure the process timeout + * + * @throws RuntimeException In case the timeout was reached + */ + public function checkTimeout() + { + if (0 < $this->timeout && $this->timeout < microtime(true) - $this->starttime) { + $this->stop(0); + + throw new RuntimeException('The process timed-out.'); + } + } + /** * Builds up the callback used by wait(). * diff --git a/src/Symfony/Component/Process/ProcessBuilder.php b/src/Symfony/Component/Process/ProcessBuilder.php index 1a95bd0df4..c4ef31d311 100644 --- a/src/Symfony/Component/Process/ProcessBuilder.php +++ b/src/Symfony/Component/Process/ProcessBuilder.php @@ -103,7 +103,7 @@ class ProcessBuilder * * To disable the timeout, set this value to null. * - * @param integer|null + * @param float|null * * @return ProcessBuilder * @@ -117,10 +117,10 @@ class ProcessBuilder return $this; } - $timeout = (integer) $timeout; + $timeout = (float) $timeout; if ($timeout < 0) { - throw new InvalidArgumentException('The timeout value must be a valid positive integer.'); + throw new InvalidArgumentException('The timeout value must be a valid positive integer or float number.'); } $this->timeout = $timeout; diff --git a/src/Symfony/Component/Process/Tests/AbstractProcessTest.php b/src/Symfony/Component/Process/Tests/AbstractProcessTest.php index 14433deeb6..ec63a39856 100644 --- a/src/Symfony/Component/Process/Tests/AbstractProcessTest.php +++ b/src/Symfony/Component/Process/Tests/AbstractProcessTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Process\Tests; use Symfony\Component\Process\Process; +use Symfony\Component\Process\Exception\RuntimeException; /** * @author Robert Schönthal @@ -44,6 +45,31 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase $this->assertNull($p->getTimeout()); } + public function testStopWithTimeoutIsActuallyWorking() + { + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + $this->markTestSkipped('Stop with timeout does not work on windows, it requires posix signals'); + } + if (!function_exists('pcntl_signal')) { + $this->markTestSkipped('This test require pcntl_signal function'); + } + + // exec is mandatory here since we send a signal to the process + // see https://github.com/symfony/symfony/issues/5030 about prepending + // command with exec + $p = $this->getProcess('exec php '.__DIR__.'/NonStopableProcess.php 3'); + $p->start(); + usleep(100000); + $start = microtime(true); + $p->stop(1.1); + while ($p->isRunning()) { + usleep(1000); + } + $duration = microtime(true) - $start; + + $this->assertLessThan(1.3, $duration); + } + /** * tests results from sub processes * @@ -320,6 +346,47 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase // PHP will deadlock when it tries to cleanup $process } + public function testRunProcessWithTimeout() + { + $timeout = 0.5; + $process = $this->getProcess('sleep 3'); + $process->setTimeout($timeout); + $start = microtime(true); + try { + $process->run(); + $this->fail('A RuntimeException should have been raised'); + } catch (RuntimeException $e) { + + } + $duration = microtime(true) - $start; + + $this->assertLessThan($timeout + Process::TIMEOUT_PRECISION, $duration); + } + + public function testCheckTimeoutOnStartedProcess() + { + $timeout = 0.5; + $precision = 100000; + $process = $this->getProcess('sleep 3'); + $process->setTimeout($timeout); + $start = microtime(true); + + $process->start(); + + try { + while ($process->isRunning()) { + $process->checkTimeout(); + usleep($precision); + } + $this->fail('A RuntimeException should have been raised'); + } catch (RuntimeException $e) { + + } + $duration = microtime(true) - $start; + + $this->assertLessThan($timeout + $precision, $duration); + } + public function responsesCodeProvider() { return array( diff --git a/src/Symfony/Component/Process/Tests/NonStopableProcess.php b/src/Symfony/Component/Process/Tests/NonStopableProcess.php new file mode 100644 index 0000000000..a4db838256 --- /dev/null +++ b/src/Symfony/Component/Process/Tests/NonStopableProcess.php @@ -0,0 +1,37 @@ + (microtime(true) - $start)) { + usleep(1000); +} diff --git a/src/Symfony/Component/Security/Acl/Dbal/AclProvider.php b/src/Symfony/Component/Security/Acl/Dbal/AclProvider.php index 6f47231ed9..822a16017c 100644 --- a/src/Symfony/Component/Security/Acl/Dbal/AclProvider.php +++ b/src/Symfony/Component/Security/Acl/Dbal/AclProvider.php @@ -263,7 +263,11 @@ SELECTCLAUSE; for ($i = 0; $i < $count; $i++) { if (!isset($types[$batch[$i]->getType()])) { $types[$batch[$i]->getType()] = true; - if ($count > 1) { + + // if there is more than one type we can safely break out of the + // loop, because it is the differentiator factor on whether to + // query for only one or more class types + if (count($types) > 1) { break; } } diff --git a/src/Symfony/Component/Security/Tests/Acl/Dbal/AclProviderTest.php b/src/Symfony/Component/Security/Tests/Acl/Dbal/AclProviderTest.php index 83771eef21..ad58d72ab2 100644 --- a/src/Symfony/Component/Security/Tests/Acl/Dbal/AclProviderTest.php +++ b/src/Symfony/Component/Security/Tests/Acl/Dbal/AclProviderTest.php @@ -72,6 +72,23 @@ class AclProviderTest extends \PHPUnit_Framework_TestCase $this->assertTrue($oids[1]->equals($acl1->getObjectIdentity())); } + public function testFindAclsWithDifferentTypes() + { + $oids = array(); + $oids[] = new ObjectIdentity('123', 'Bundle\SomeVendor\MyBundle\Entity\SomeEntity'); + $oids[] = new ObjectIdentity('123', 'Bundle\MyBundle\Entity\AnotherEntity'); + + $provider = $this->getProvider(); + + $acls = $provider->findAcls($oids); + $this->assertInstanceOf('SplObjectStorage', $acls); + $this->assertCount(2, $acls); + $this->assertInstanceOf('Symfony\Component\Security\Acl\Domain\Acl', $acl0 = $acls->offsetGet($oids[0])); + $this->assertInstanceOf('Symfony\Component\Security\Acl\Domain\Acl', $acl1 = $acls->offsetGet($oids[1])); + $this->assertTrue($oids[0]->equals($acl0->getObjectIdentity())); + $this->assertTrue($oids[1]->equals($acl1->getObjectIdentity())); + } + public function testFindAclCachesAclInMemory() { $oid = new ObjectIdentity('1', 'foo');